mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Final merge of bug 596451. a=blocking2.0 - Asynchronous layer-based painting on Windows. Various pieces r=josh/romaxa/karlt/jmathies/roc.
This merge includes the fix for bug 583109 - Add visibility notifications for plugins, so that asynchronous painting can be suspended when plugins are not visible. This collaterally fixes bug 606285, hulu homepage doesn't paint with async layers. This changeset only implements asynchronous painting for out-of-process plugins. In-process painting will be implemented as a followup. Note that it is still possible to disable asynchronous layers on Windows by setting the preference mozilla.plugins.use_layers to false, but my intention is to remove this preference and sync-painting codepaths soon in a followup.
This commit is contained in:
commit
abe5e4d05f
@ -2730,6 +2730,9 @@ nsAccessible::InvalidateChildren()
|
||||
PRBool
|
||||
nsAccessible::AppendChild(nsAccessible* aChild)
|
||||
{
|
||||
if (!aChild)
|
||||
return PR_FALSE;
|
||||
|
||||
if (!mChildren.AppendElement(aChild))
|
||||
return PR_FALSE;
|
||||
|
||||
@ -2743,11 +2746,16 @@ nsAccessible::AppendChild(nsAccessible* aChild)
|
||||
PRBool
|
||||
nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
|
||||
{
|
||||
if (!aChild)
|
||||
return PR_FALSE;
|
||||
|
||||
if (!mChildren.InsertElementAt(aIndex, aChild))
|
||||
return PR_FALSE;
|
||||
|
||||
for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++)
|
||||
mChildren[idx]->mIndexInParent++;
|
||||
for (PRUint32 idx = aIndex + 1; idx < mChildren.Length(); idx++) {
|
||||
NS_ASSERTION(mChildren[idx]->mIndexInParent == idx - 1, "Accessible child index doesn't match");
|
||||
mChildren[idx]->mIndexInParent = idx;
|
||||
}
|
||||
|
||||
if (nsAccUtils::IsText(aChild))
|
||||
mChildrenFlags = eMixedChildren;
|
||||
@ -2761,23 +2769,28 @@ nsAccessible::InsertChildAt(PRUint32 aIndex, nsAccessible* aChild)
|
||||
PRBool
|
||||
nsAccessible::RemoveChild(nsAccessible* aChild)
|
||||
{
|
||||
if (aChild->mParent != this || aChild->mIndexInParent == -1)
|
||||
if (!aChild)
|
||||
return PR_FALSE;
|
||||
|
||||
if (aChild->mIndexInParent >= mChildren.Length() ||
|
||||
mChildren[aChild->mIndexInParent] != aChild) {
|
||||
PRInt32 index = aChild->mIndexInParent;
|
||||
if (aChild->mParent != this || index == -1)
|
||||
return PR_FALSE;
|
||||
|
||||
if (index >= mChildren.Length() || mChildren[index] != aChild) {
|
||||
NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
|
||||
aChild->UnbindFromParent();
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
for (PRUint32 idx = aChild->mIndexInParent + 1; idx < mChildren.Length(); idx++)
|
||||
mChildren[idx]->mIndexInParent--;
|
||||
|
||||
mChildren.RemoveElementAt(aChild->mIndexInParent);
|
||||
mEmbeddedObjCollector = nsnull;
|
||||
for (PRUint32 idx = index + 1; idx < mChildren.Length(); idx++) {
|
||||
NS_ASSERTION(mChildren[idx]->mIndexInParent == idx, "Accessible child index doesn't match");
|
||||
mChildren[idx]->mIndexInParent = idx - 1;
|
||||
}
|
||||
|
||||
aChild->UnbindFromParent();
|
||||
mChildren.RemoveElementAt(index);
|
||||
mEmbeddedObjCollector = nsnull;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
@ -679,8 +679,10 @@ nsDocAccessible::Shutdown()
|
||||
mParent->RemoveChild(this);
|
||||
}
|
||||
|
||||
PRUint32 childDocCount = mChildDocuments.Length();
|
||||
for (PRUint32 idx = 0; idx < childDocCount; idx++)
|
||||
// Walk the array backwards because child documents remove themselves from the
|
||||
// array as they are shutdown.
|
||||
PRInt32 childDocCount = mChildDocuments.Length();
|
||||
for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--)
|
||||
mChildDocuments[idx]->Shutdown();
|
||||
|
||||
mChildDocuments.Clear();
|
||||
|
@ -196,8 +196,13 @@ nsOuterDocAccessible::InvalidateChildren()
|
||||
PRBool
|
||||
nsOuterDocAccessible::AppendChild(nsAccessible *aAccessible)
|
||||
{
|
||||
NS_ASSERTION(!mChildren.Length(),
|
||||
"Previous child document of outerdoc accessible wasn't removed!");
|
||||
// We keep showing the old document for a bit after creating the new one,
|
||||
// and while building the new DOM and frame tree. That's done on purpose
|
||||
// to avoid weird flashes of default background color.
|
||||
// The old viewer will be destroyed after the new one is created.
|
||||
// For a11y, it should be safe to shut down the old document now.
|
||||
if (mChildren.Length())
|
||||
mChildren[0]->Shutdown();
|
||||
|
||||
if (!nsAccessible::AppendChild(aAccessible))
|
||||
return PR_FALSE;
|
||||
|
@ -51,7 +51,7 @@ tabbrowser {
|
||||
-moz-transition: opacity .25s;
|
||||
}
|
||||
|
||||
.tabbrowser-tab[pinned] {
|
||||
.tabbrowser-tabs:not([pinnedonly]) > .tabbrowser-tab[pinned] {
|
||||
position: fixed;
|
||||
display: block; /* position:fixed already does this (bug 579776), but let's be explicit */
|
||||
}
|
||||
|
@ -832,31 +832,48 @@
|
||||
// Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
|
||||
if (!this._previewMode) {
|
||||
// We've selected the new tab, so go ahead and notify listeners.
|
||||
var event = document.createEvent("Events");
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TabSelect", true, false);
|
||||
this.mCurrentTab.dispatchEvent(event);
|
||||
|
||||
this._tabAttrModified(oldTab);
|
||||
this._tabAttrModified(this.mCurrentTab);
|
||||
|
||||
// Change focus to the new browser unless the findbar is focused.
|
||||
if (!gFindBarInitialized ||
|
||||
gFindBar.hidden ||
|
||||
gFindBar.getElement("findbar-textbox").getAttribute("focused") != "true") {
|
||||
// Adjust focus
|
||||
do {
|
||||
// Focus the location bar if it was previously focused for that tab.
|
||||
// In full screen mode, only bother making the location bar visible
|
||||
// if the tab is a blank one.
|
||||
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
|
||||
if (newBrowser._urlbarFocused && gURLBar) {
|
||||
if (!window.fullScreen) {
|
||||
gURLBar.focus();
|
||||
break;
|
||||
} else if (isTabEmpty(this.mCurrentTab)) {
|
||||
focusAndSelectUrlBar();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var fm = Components.classes["@mozilla.org/focus-manager;1"].
|
||||
getService(Components.interfaces.nsIFocusManager);
|
||||
var newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
|
||||
// If the find bar is focused, keep it focused.
|
||||
if (gFindBarInitialized &&
|
||||
!gFindBar.hidden &&
|
||||
gFindBar.getElement("findbar-textbox").getAttribute("focused") == "true")
|
||||
break;
|
||||
|
||||
// Otherwise, focus the content area.
|
||||
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||
let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
|
||||
|
||||
// for anchors, use FLAG_SHOWRING so that it is clear what link was
|
||||
// last clicked when switching back to that tab
|
||||
var focusFlags = fm.FLAG_NOSCROLL;
|
||||
let focusFlags = fm.FLAG_NOSCROLL;
|
||||
if (newFocusedElement &&
|
||||
(newFocusedElement instanceof HTMLAnchorElement ||
|
||||
newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
|
||||
focusFlags |= fm.FLAG_SHOWRING;
|
||||
fm.setFocus(newBrowser, focusFlags);
|
||||
}
|
||||
} while (false);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
@ -1902,15 +1919,11 @@
|
||||
<parameter name="aTab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.visibleTabs.length == 1)
|
||||
if (this.tabs.length == 1)
|
||||
return null;
|
||||
|
||||
// tell a new window to take the "dropped" tab
|
||||
return Services.ww.openWindow(window,
|
||||
getBrowserURL(),
|
||||
null,
|
||||
"chrome,dialog=no,all",
|
||||
aTab);
|
||||
return window.openDialog(getBrowserURL(), "_blank", "dialog=no,all", aTab);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
@ -2708,11 +2721,18 @@
|
||||
<method name="_positionPinnedTabs">
|
||||
<body><![CDATA[
|
||||
var width = 0;
|
||||
var scrollButtonWidth = this.getAttribute("overflow") != "true" ? 0 :
|
||||
var pinnedOnly = (this.tabbrowser._numPinnedTabs == this.tabbrowser.visibleTabs.length);
|
||||
|
||||
if (pinnedOnly)
|
||||
this.tabbrowser.tabContainer.setAttribute("pinnedonly", "true");
|
||||
else
|
||||
this.tabbrowser.tabContainer.removeAttribute("pinnedonly");
|
||||
|
||||
var scrollButtonWidth = (this.getAttribute("overflow") != "true" || pinnedOnly) ? 0 :
|
||||
this.mTabstrip._scrollButtonDown.scrollWidth;
|
||||
for (var i = this.tabbrowser._numPinnedTabs - 1; i >= 0; i--) {
|
||||
let tab = this.childNodes[i];
|
||||
width += tab.scrollWidth;
|
||||
width += pinnedOnly ? 0 : tab.scrollWidth;
|
||||
tab.style.MozMarginStart = - (width + scrollButtonWidth) + "px";
|
||||
}
|
||||
this.style.MozMarginStart = width + "px";
|
||||
|
@ -1020,19 +1020,22 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
|
||||
// ----------
|
||||
// Function: shouldStack
|
||||
// Returns true if the groupItem, given "count", should stack (instead of grid).
|
||||
// Returns true if the groupItem should stack (instead of grid).
|
||||
shouldStack: function GroupItem_shouldStack(count) {
|
||||
if (count <= 1)
|
||||
return false;
|
||||
|
||||
var bb = this.getContentBounds();
|
||||
var options = {
|
||||
pretend: true,
|
||||
count: count
|
||||
return: 'widthAndColumns',
|
||||
count: count || this._children.length
|
||||
};
|
||||
let {childWidth, columns} = Items.arrange(null, bb, options);
|
||||
|
||||
var rects = Items.arrange(null, bb, options);
|
||||
return (rects[0].width < 55);
|
||||
let shouldStack = childWidth < TabItems.minTabWidth * 1.35;
|
||||
this._columns = shouldStack ? null : columns;
|
||||
|
||||
return shouldStack;
|
||||
},
|
||||
|
||||
// ----------
|
||||
@ -1049,35 +1052,30 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
Items.arrange(this._children, box, Utils.extend({}, options, {z: 99999}));
|
||||
} else {
|
||||
var bb = this.getContentBounds();
|
||||
var count = this._children.length;
|
||||
if (!this.shouldStack(count)) {
|
||||
if (!this.shouldStack()) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
var animate;
|
||||
if (typeof options.animate == 'undefined')
|
||||
animate = true;
|
||||
else
|
||||
animate = options.animate;
|
||||
|
||||
this._children.forEach(function(child) {
|
||||
child.removeClass("stacked")
|
||||
});
|
||||
|
||||
this.topChild = null;
|
||||
|
||||
var arrangeOptions = Utils.copy(options);
|
||||
Utils.extend(arrangeOptions, {
|
||||
pretend: true,
|
||||
count: count
|
||||
});
|
||||
|
||||
if (!count) {
|
||||
if (!this._children.length) {
|
||||
this.xDensity = 0;
|
||||
this.yDensity = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var arrangeOptions = Utils.copy(options);
|
||||
Utils.extend(arrangeOptions, {
|
||||
columns: this._columns
|
||||
});
|
||||
|
||||
// Items.arrange will rearrange the children, but also return an array
|
||||
// of the Rect's used.
|
||||
|
||||
var rects = Items.arrange(this._children, bb, arrangeOptions);
|
||||
|
||||
// yDensity = (the distance of the bottom of the last tab to the top of the content area)
|
||||
@ -1098,15 +1096,6 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
}
|
||||
this.xDensity = (rightMostRight - bb.left) / (bb.width);
|
||||
|
||||
this._children.forEach(function(child, index) {
|
||||
if (!child.locked.bounds) {
|
||||
child.setBounds(rects[index], !animate);
|
||||
child.setRotation(0);
|
||||
if (options.z)
|
||||
child.setZ(options.z);
|
||||
}
|
||||
});
|
||||
|
||||
this._isStacked = false;
|
||||
} else
|
||||
this._stackArrange(bb, options);
|
||||
|
@ -601,7 +601,7 @@ Item.prototype = {
|
||||
// ___ mousemove
|
||||
var handleMouseMove = function(e) {
|
||||
// positioning
|
||||
var mouse = new Point(e.pageX, e.pageY);
|
||||
var mouse = new Point(e.pageX, e.pageY);
|
||||
if (!startSent) {
|
||||
if(Math.abs(mouse.x - startMouse.x) > self.dragOptions.minDragDistance ||
|
||||
Math.abs(mouse.y - startMouse.y) > self.dragOptions.minDragDistance) {
|
||||
@ -892,39 +892,41 @@ let Items = {
|
||||
// maximizing item size but maintaining standard tab aspect ratio for each
|
||||
//
|
||||
// Parameters:
|
||||
// items - an array of <Item>s. Can be null if the pretend and count options are set.
|
||||
// items - an array of <Item>s. Can be null, in which case we won't
|
||||
// actually move anything.
|
||||
// bounds - a <Rect> defining the space to arrange within
|
||||
// options - an object with various properites (see below)
|
||||
//
|
||||
// Possible "options" properties:
|
||||
// animate - whether to animate; default: true.
|
||||
// z - the z index to set all the items; default: don't change z.
|
||||
// pretend - whether to collect and return the rectangle rather than moving the items; default: false
|
||||
// count - overrides the item count for layout purposes; default: the actual item count
|
||||
// return - if set to 'widthAndColumns', it'll return an object with the
|
||||
// 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
|
||||
//
|
||||
// Returns:
|
||||
// the list of rectangles if the pretend option is set; otherwise null
|
||||
// an object with the width value of the child items and the number of columns,
|
||||
// if the return option is set to 'widthAndColumns'; otherwise the list of <Rect>s
|
||||
arrange: function Items_arrange(items, bounds, options) {
|
||||
var animate;
|
||||
if (!options || typeof options.animate == 'undefined')
|
||||
animate = true;
|
||||
else
|
||||
animate = options.animate;
|
||||
|
||||
if (typeof options == 'undefined')
|
||||
options = {};
|
||||
|
||||
var rects = null;
|
||||
if (options.pretend)
|
||||
rects = [];
|
||||
var animate = true;
|
||||
if (typeof options.animate != 'undefined')
|
||||
animate = options.animate;
|
||||
var immediately = !animate;
|
||||
|
||||
var rects = [];
|
||||
|
||||
var tabAspect = TabItems.tabHeight / TabItems.tabWidth;
|
||||
var count = options.count || (items ? items.length : 0);
|
||||
if (!count)
|
||||
return rects;
|
||||
|
||||
var columns = 1;
|
||||
var columns = options.columns || 1;
|
||||
// We'll assume for the time being that all the items have the same styling
|
||||
// and that the margin is the same width around.
|
||||
var itemMargin = items && items.length ?
|
||||
@ -954,20 +956,17 @@ let Items = {
|
||||
tabWidth = Math.min(tabWidth, (bounds.height - 2 * itemMargin) / tabAspect);
|
||||
tabHeight = tabWidth * tabAspect;
|
||||
}
|
||||
|
||||
if (options.return == 'widthAndColumns')
|
||||
return {childWidth: tabWidth, columns: columns};
|
||||
|
||||
var box = new Rect(bounds.left, bounds.top, tabWidth, tabHeight);
|
||||
var row = 0;
|
||||
var column = 0;
|
||||
var immediately;
|
||||
|
||||
var a;
|
||||
for (a = 0; a < count; a++) {
|
||||
immediately = !animate;
|
||||
|
||||
if (rects)
|
||||
rects.push(new Rect(box));
|
||||
else if (items && a < items.length) {
|
||||
var item = items[a];
|
||||
for (let a = 0; a < count; a++) {
|
||||
rects.push(new Rect(box));
|
||||
if (items && a < items.length) {
|
||||
let item = items[a];
|
||||
if (!item.locked.bounds) {
|
||||
item.setBounds(box, immediately);
|
||||
item.setRotation(0);
|
||||
@ -982,7 +981,6 @@ let Items = {
|
||||
box.left = bounds.left;
|
||||
box.top += (box.height * yScale) + padding;
|
||||
column = 0;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,6 +145,7 @@ _BROWSER_FILES = \
|
||||
browser_bug561636.js \
|
||||
browser_bug562649.js \
|
||||
browser_bug563588.js \
|
||||
browser_bug565575.js \
|
||||
browser_bug575561.js \
|
||||
browser_bug577121.js \
|
||||
browser_bug579872.js \
|
||||
@ -159,6 +160,7 @@ _BROWSER_FILES = \
|
||||
browser_bug595507.js \
|
||||
browser_bug596687.js \
|
||||
browser_bug597218.js \
|
||||
browser_bug609700.js \
|
||||
browser_contextSearchTabPosition.js \
|
||||
browser_ctrlTab.js \
|
||||
browser_discovery.js \
|
||||
|
13
browser/base/content/test/browser_bug565575.js
Normal file
13
browser/base/content/test/browser_bug565575.js
Normal file
@ -0,0 +1,13 @@
|
||||
function test() {
|
||||
gBrowser.selectedBrowser.focus();
|
||||
BrowserOpenTab();
|
||||
ok(gURLBar.focused, "location bar is focused for a new tab");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
ok(!gURLBar.focused, "location bar isn't focused for the previously selected tab");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[1];
|
||||
ok(gURLBar.focused, "location bar is re-focused when selecting the new tab");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
20
browser/base/content/test/browser_bug609700.js
Normal file
20
browser/base/content/test/browser_bug609700.js
Normal file
@ -0,0 +1,20 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowopened") {
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
|
||||
ok(true, "duplicateTabIn opened a new window");
|
||||
|
||||
aSubject.addEventListener("load", function () {
|
||||
executeSoon(function () {
|
||||
aSubject.close();
|
||||
finish();
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
});
|
||||
|
||||
duplicateTabIn(gBrowser.selectedTab, "window");
|
||||
}
|
@ -44,11 +44,8 @@ function test() {
|
||||
browser2.contentWindow, null, true,
|
||||
"focusedElement after tab change, focus in new tab");
|
||||
|
||||
// switching tabs when the urlbar is focused and nothing in the new tab is focused
|
||||
// switching tabs when nothing in the new tab is focused
|
||||
// should focus the browser
|
||||
expectFocusShift(function () gURLBar.focus(),
|
||||
window, gURLBar.inputField, true,
|
||||
"url field focused");
|
||||
expectFocusShift(function () gBrowser.selectedTab = tab1,
|
||||
browser1.contentWindow, null, true,
|
||||
"focusedElement after tab change, focus in new tab");
|
||||
@ -89,6 +86,9 @@ function test() {
|
||||
window, gURLBar.inputField, true,
|
||||
"focusedWindow after url field focused");
|
||||
is(fm.getFocusedElementForWindow(browser2.contentWindow, false, {}), button2, "url field focused, button in tab");
|
||||
expectFocusShift(function () gURLBar.blur(),
|
||||
window, null, true,
|
||||
"focusedWindow after browser focused");
|
||||
|
||||
// when a chrome element is focused, switching tabs to a tab with a button
|
||||
// with the current focus should focus the button
|
||||
|
@ -37,6 +37,8 @@
|
||||
const kIMig = Components.interfaces.nsIBrowserProfileMigrator;
|
||||
const kIPStartup = Components.interfaces.nsIProfileStartup;
|
||||
const kProfileMigratorContractIDPrefix = "@mozilla.org/profile/migrator;1?app=browser&type=";
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var MigrationWizard = {
|
||||
_source: "", // Source Profile Migrator ContractID suffix
|
||||
@ -54,6 +56,7 @@ var MigrationWizard = {
|
||||
os.addObserver(this, "Migration:Started", false);
|
||||
os.addObserver(this, "Migration:ItemBeforeMigrate", false);
|
||||
os.addObserver(this, "Migration:ItemAfterMigrate", false);
|
||||
os.addObserver(this, "Migration:ItemError", false);
|
||||
os.addObserver(this, "Migration:Ended", false);
|
||||
|
||||
this._wiz = document.documentElement;
|
||||
@ -81,6 +84,7 @@ var MigrationWizard = {
|
||||
os.removeObserver(this, "Migration:Started");
|
||||
os.removeObserver(this, "Migration:ItemBeforeMigrate");
|
||||
os.removeObserver(this, "Migration:ItemAfterMigrate");
|
||||
os.removeObserver(this, "Migration:ItemError");
|
||||
os.removeObserver(this, "Migration:Ended");
|
||||
},
|
||||
|
||||
@ -489,6 +493,35 @@ var MigrationWizard = {
|
||||
nextButton.click();
|
||||
}
|
||||
break;
|
||||
case "Migration:ItemError":
|
||||
var type = "undefined";
|
||||
switch (parseInt(aData)) {
|
||||
case Ci.nsIBrowserProfileMigrator.SETTINGS:
|
||||
type = "settings";
|
||||
break;
|
||||
case Ci.nsIBrowserProfileMigrator.COOKIES:
|
||||
type = "cookies";
|
||||
break;
|
||||
case Ci.nsIBrowserProfileMigrator.HISTORY:
|
||||
type = "history";
|
||||
break;
|
||||
case Ci.nsIBrowserProfileMigrator.FORMDATA:
|
||||
type = "form data";
|
||||
break;
|
||||
case Ci.nsIBrowserProfileMigrator.PASSWORDS:
|
||||
type = "passwords";
|
||||
break;
|
||||
case Ci.nsIBrowserProfileMigrator.BOOKMARKS:
|
||||
type = "bookmarks";
|
||||
break;
|
||||
case Ci.nsIBrowserProfileMigrator.OTHERDATA:
|
||||
type = "misc. data";
|
||||
break;
|
||||
}
|
||||
Cc["@mozilla.org/consoleservice;1"]
|
||||
.getService(Ci.nsIConsoleService)
|
||||
.logStringMessage("some " + type + " did not successfully migrate.");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#define browserprofilemigratorutils___h___
|
||||
|
||||
#define MIGRATION_ITEMBEFOREMIGRATE "Migration:ItemBeforeMigrate"
|
||||
#define MIGRATION_ITEMMIGRATEERROR "Migration:ItemError"
|
||||
#define MIGRATION_ITEMAFTERMIGRATE "Migration:ItemAfterMigrate"
|
||||
#define MIGRATION_STARTED "Migration:Started"
|
||||
#define MIGRATION_ENDED "Migration:Ended"
|
||||
@ -47,11 +48,12 @@
|
||||
mObserverService->NotifyObservers(nsnull, message, item)
|
||||
|
||||
#define COPY_DATA(func, replace, itemIndex) \
|
||||
if (NS_SUCCEEDED(rv) && (aItems & itemIndex || !aItems)) { \
|
||||
if ((aItems & itemIndex || !aItems)) { \
|
||||
nsAutoString index; \
|
||||
index.AppendInt(itemIndex); \
|
||||
NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); \
|
||||
rv = func(replace); \
|
||||
if (NS_FAILED(func(replace))) \
|
||||
NOTIFY_OBSERVERS(MIGRATION_ITEMMIGRATEERROR, index.get()); \
|
||||
NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); \
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,8 @@
|
||||
#include "nsILocalFileWin.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
#include "prnetdb.h"
|
||||
|
||||
#include <objbase.h>
|
||||
#include <shlguid.h>
|
||||
#include <urlhist.h>
|
||||
@ -1919,14 +1921,20 @@ nsIEProfileMigrator::CopyCookiesFromBuffer(char *aBuffer,
|
||||
nsDependentCString stringName(name),
|
||||
stringPath(path);
|
||||
|
||||
// delete any possible extant matching host cookie
|
||||
if (hostCopy[0] == '.')
|
||||
// delete any possible extant matching host cookie and
|
||||
// check if we're dealing with an IPv4/IPv6 hostname.
|
||||
PRBool isIPAddress = PR_FALSE;
|
||||
if (hostCopy[0] == '.') {
|
||||
aCookieManager->Remove(nsDependentCString(hostCopy+1),
|
||||
stringName, stringPath, PR_FALSE);
|
||||
PRNetAddr addr;
|
||||
if (PR_StringToNetAddr(hostCopy+1, &addr) == PR_SUCCESS)
|
||||
isIPAddress = PR_TRUE;
|
||||
}
|
||||
|
||||
nsresult onerv;
|
||||
// Add() makes a new domain cookie
|
||||
onerv = aCookieManager->Add(nsDependentCString(hostCopy),
|
||||
onerv = aCookieManager->Add(nsDependentCString(hostCopy + (isIPAddress ? 1 : 0)),
|
||||
stringPath,
|
||||
stringName,
|
||||
nsDependentCString(value),
|
||||
|
@ -745,24 +745,59 @@ SessionStoreService.prototype = {
|
||||
!this._inPrivateBrowsing) {
|
||||
// default to the most-recently closed window
|
||||
// don't use popup windows
|
||||
let state = null;
|
||||
let newClosedWindows = this._closedWindows.filter(function(aWinState) {
|
||||
if (!state && !aWinState.isPopup) {
|
||||
state = aWinState;
|
||||
return false;
|
||||
let closedWindowState = null;
|
||||
let closedWindowIndex;
|
||||
for (let i = 0; i < this._closedWindows.length; i++) {
|
||||
// Take the first non-popup, point our object at it, and break out.
|
||||
if (!this._closedWindows[i].isPopup) {
|
||||
closedWindowState = this._closedWindows[i];
|
||||
closedWindowIndex = i;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (state) {
|
||||
delete state.hidden;
|
||||
}
|
||||
|
||||
if (closedWindowState) {
|
||||
let newWindowState;
|
||||
#ifndef XP_MACOSX
|
||||
if (!this._doResumeSession())
|
||||
if (!this._doResumeSession()) {
|
||||
#endif
|
||||
state.tabs = state.tabs.filter(function (tab) tab.pinned);
|
||||
if (state.tabs.length > 0) {
|
||||
this._closedWindows = newClosedWindows;
|
||||
// We want to split the window up into pinned tabs and unpinned tabs.
|
||||
// Pinned tabs should be restored. If there are any remaining tabs,
|
||||
// they should be added back to _closedWindows.
|
||||
// We'll cheat a little bit and reuse _prepDataForDeferredRestore
|
||||
// even though it wasn't built exactly for this.
|
||||
let [appTabsState, normalTabsState] =
|
||||
this._prepDataForDeferredRestore(JSON.stringify({ windows: [closedWindowState] }));
|
||||
|
||||
// These are our pinned tabs, which we should restore
|
||||
if (appTabsState.windows.length) {
|
||||
newWindowState = appTabsState.windows[0];
|
||||
delete newWindowState.__lastSessionWindowID;
|
||||
}
|
||||
|
||||
// In case there were no unpinned tabs, remove the window from _closedWindows
|
||||
if (!normalTabsState.windows.length) {
|
||||
this._closedWindows.splice(closedWindowIndex, 1);
|
||||
}
|
||||
// Or update _closedWindows with the modified state
|
||||
else {
|
||||
delete normalTabsState.windows[0].__lastSessionWindowID;
|
||||
this._closedWindows[closedWindowIndex] = normalTabsState.windows[0];
|
||||
}
|
||||
#ifndef XP_MACOSX
|
||||
}
|
||||
else {
|
||||
// If we're just restoring the window, make sure it gets removed from
|
||||
// _closedWindows.
|
||||
this._closedWindows.splice(closedWindowIndex, 1);
|
||||
newWindowState = closedWindowState;
|
||||
delete newWindowState.hidden;
|
||||
}
|
||||
#endif
|
||||
if (newWindowState) {
|
||||
// Ensure that the window state isn't hidden
|
||||
this._restoreCount = 1;
|
||||
state = { windows: [state] };
|
||||
let state = { windows: [newWindowState] };
|
||||
this.restoreWindow(aWindow, state, this._isCmdLineEmpty(aWindow, state));
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ _BROWSER_TEST_FILES = \
|
||||
browser_581593.js \
|
||||
browser_586147.js \
|
||||
browser_586068-cascaded_restore.js \
|
||||
browser_589246.js \
|
||||
browser_590268.js \
|
||||
browser_600545.js \
|
||||
$(NULL)
|
||||
|
276
browser/components/sessionstore/test/browser/browser_589246.js
Normal file
276
browser/components/sessionstore/test/browser/browser_589246.js
Normal file
@ -0,0 +1,276 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is sessionstore test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Paul O’Shannessy <paul@oshannessy.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Mirrors WINDOW_ATTRIBUTES IN nsSessionStore.js
|
||||
const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
|
||||
getService(Ci.nsISessionStore);
|
||||
|
||||
let stateBackup = ss.getBrowserState();
|
||||
|
||||
let originalWarnOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose");
|
||||
let originalStartupPage = gPrefService.getIntPref("browser.startup.page");
|
||||
let originalWindowType = document.documentElement.getAttribute("windowtype");
|
||||
|
||||
let gotLastWindowClosedTopic = false;
|
||||
let shouldPinTab = false;
|
||||
let shouldOpenTabs = false;
|
||||
let shouldCloseTab = false;
|
||||
let testNum = 0;
|
||||
let afterTestCallback;
|
||||
|
||||
// Set state so we know the closed windows content
|
||||
let testState = {
|
||||
windows: [
|
||||
{ tabs: [{ entries: [{ url: "http://example.org" }] }] }
|
||||
],
|
||||
_closedWindows: []
|
||||
};
|
||||
|
||||
// We'll push a set of conditions and callbacks into this array
|
||||
// Ideally we would also test win/linux under a complete set of conditions, but
|
||||
// the tests for osx mirror the other set of conditions possible on win/linux.
|
||||
let tests = [];
|
||||
|
||||
// the third & fourth test share a condition check, keep it DRY
|
||||
function checkOSX34Generator(num) {
|
||||
return function(aPreviousState, aCurState) {
|
||||
// In here, we should have restored the pinned tab, so only the unpinned tab
|
||||
// should be in aCurState. So let's shape our expectations.
|
||||
let expectedState = JSON.parse(aPreviousState);
|
||||
expectedState[0].tabs.shift();
|
||||
// size attributes are stripped out in _prepDataForDeferredRestore in nsSessionStore.
|
||||
// This isn't the best approach, but neither is comparing JSON strings
|
||||
WINDOW_ATTRIBUTES.forEach(function (attr) delete expectedState[0][attr]);
|
||||
|
||||
is(aCurState, JSON.stringify(expectedState),
|
||||
"test #" + num + ": closedWindowState is as expected");
|
||||
};
|
||||
}
|
||||
function checkNoWindowsGenerator(num) {
|
||||
return function(aPreviousState, aCurState) {
|
||||
is(aCurState, "[]", "test #" + num + ": there should be no closedWindowsLeft");
|
||||
};
|
||||
}
|
||||
|
||||
// The first test has 0 pinned tabs and 1 unpinned tab
|
||||
tests.push({
|
||||
pinned: false,
|
||||
extra: false,
|
||||
close: false,
|
||||
checkWinLin: checkNoWindowsGenerator(1),
|
||||
checkOSX: function(aPreviousState, aCurState) {
|
||||
is(aCurState, aPreviousState, "test #1: closed window state is unchanged");
|
||||
}
|
||||
});
|
||||
|
||||
// The second test has 1 pinned tab and 0 unpinned tabs.
|
||||
tests.push({
|
||||
pinned: true,
|
||||
extra: false,
|
||||
close: false,
|
||||
checkWinLin: checkNoWindowsGenerator(2),
|
||||
checkOSX: checkNoWindowsGenerator(2)
|
||||
});
|
||||
|
||||
// The third test has 1 pinned tab and 2 unpinned tabs.
|
||||
tests.push({
|
||||
pinned: true,
|
||||
extra: true,
|
||||
close: false,
|
||||
checkWinLin: checkNoWindowsGenerator(3),
|
||||
checkOSX: checkOSX34Generator(3)
|
||||
});
|
||||
|
||||
// The fourth test has 1 pinned tab, 2 unpinned tabs, and closes one unpinned tab.
|
||||
tests.push({
|
||||
pinned: true,
|
||||
extra: true,
|
||||
close: "one",
|
||||
checkWinLin: checkNoWindowsGenerator(4),
|
||||
checkOSX: checkOSX34Generator(4)
|
||||
});
|
||||
|
||||
// The fifth test has 1 pinned tab, 2 unpinned tabs, and closes both unpinned tabs.
|
||||
tests.push({
|
||||
pinned: true,
|
||||
extra: true,
|
||||
close: "both",
|
||||
checkWinLin: checkNoWindowsGenerator(5),
|
||||
checkOSX: checkNoWindowsGenerator(5)
|
||||
});
|
||||
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 589246 - Closed window state getting corrupted when closing
|
||||
and reopening last browser window without exiting browser **/
|
||||
waitForExplicitFinish();
|
||||
// windows opening & closing, so extending the timeout
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// We don't want the quit dialog pref
|
||||
gPrefService.setBoolPref("browser.tabs.warnOnClose", false);
|
||||
// Ensure that we would restore the session (important for Windows)
|
||||
gPrefService.setIntPref("browser.startup.page", 3);
|
||||
|
||||
runNextTestOrFinish();
|
||||
}
|
||||
|
||||
function runNextTestOrFinish() {
|
||||
if (tests.length) {
|
||||
setupForTest(tests.shift())
|
||||
}
|
||||
else {
|
||||
// some state is cleaned up at the end of each test, but not all
|
||||
["browser.tabs.warnOnClose", "browser.startup.page"].forEach(function(p) {
|
||||
if (gPrefService.prefHasUserValue(p))
|
||||
gPrefService.clearUserPref(p);
|
||||
});
|
||||
|
||||
ss.setBrowserState(stateBackup);
|
||||
executeSoon(finish);
|
||||
}
|
||||
}
|
||||
|
||||
function setupForTest(aConditions) {
|
||||
// reset some checks
|
||||
gotLastWindowClosedTopic = false;
|
||||
shouldPinTab = aConditions.pinned;
|
||||
shouldOpenTabs = aConditions.extra;
|
||||
shouldCloseTab = aConditions.close;
|
||||
testNum++;
|
||||
|
||||
// set our test callback
|
||||
afterTestCallback = /Mac/.test(navigator.platform) ? aConditions.checkOSX
|
||||
: aConditions.checkWinLin;
|
||||
|
||||
// Add observers
|
||||
Services.obs.addObserver(onLastWindowClosed, "browser-lastwindow-close-granted", false);
|
||||
|
||||
// Set the state
|
||||
Services.obs.addObserver(onStateRestored, "sessionstore-browser-state-restored", false);
|
||||
ss.setBrowserState(JSON.stringify(testState));
|
||||
}
|
||||
|
||||
function onStateRestored(aSubject, aTopic, aData) {
|
||||
info("test #" + testNum + ": onStateRestored");
|
||||
Services.obs.removeObserver(onStateRestored, "sessionstore-browser-state-restored", false);
|
||||
|
||||
// change this window's windowtype so that closing a new window will trigger
|
||||
// browser-lastwindow-close-granted.
|
||||
document.documentElement.setAttribute("windowtype", "navigator:testrunner");
|
||||
|
||||
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com");
|
||||
newWin.addEventListener("load", function(aEvent) {
|
||||
newWin.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
newWin.gBrowser.selectedBrowser.addEventListener("load", function() {
|
||||
newWin.gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
// pin this tab
|
||||
if (shouldPinTab)
|
||||
newWin.gBrowser.pinTab(newWin.gBrowser.selectedTab);
|
||||
|
||||
newWin.addEventListener("unload", onWindowUnloaded, false);
|
||||
// Open a new tab as well. On Windows/Linux this will be restored when the
|
||||
// new window is opened below (in onWindowUnloaded). On OS X we'll just
|
||||
// restore the pinned tabs, leaving the unpinned tab in the closedWindowsData.
|
||||
if (shouldOpenTabs) {
|
||||
let newTab = newWin.gBrowser.addTab("about:config");
|
||||
let newTab2 = newWin.gBrowser.addTab("about:buildconfig");
|
||||
|
||||
newTab.linkedBrowser.addEventListener("load", function() {
|
||||
newTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
if (shouldCloseTab == "one") {
|
||||
newWin.gBrowser.removeTab(newTab2);
|
||||
}
|
||||
else if (shouldCloseTab == "both") {
|
||||
newWin.gBrowser.removeTab(newTab);
|
||||
newWin.gBrowser.removeTab(newTab2);
|
||||
}
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
}, true);
|
||||
}
|
||||
else {
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
}
|
||||
}, true);
|
||||
}, false);
|
||||
}
|
||||
|
||||
// This will be called before the window is actually closed
|
||||
function onLastWindowClosed(aSubject, aTopic, aData) {
|
||||
info("test #" + testNum + ": onLastWindowClosed");
|
||||
Services.obs.removeObserver(onLastWindowClosed, "browser-lastwindow-close-granted", false);
|
||||
gotLastWindowClosedTopic = true;
|
||||
}
|
||||
|
||||
// This is the unload event listener on the new window (from onStateRestored).
|
||||
// Unload is fired after the window is closed, so sessionstore has already
|
||||
// updated _closedWindows (which is important). We'll open a new window here
|
||||
// which should actually trigger the bug.
|
||||
function onWindowUnloaded() {
|
||||
info("test #" + testNum + ": onWindowClosed");
|
||||
ok(gotLastWindowClosedTopic, "test #" + testNum + ": browser-lastwindow-close-granted was notified prior");
|
||||
|
||||
let previousClosedWindowData = ss.getClosedWindowData();
|
||||
|
||||
// Now we want to open a new window
|
||||
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no", "about:robots");
|
||||
newWin.addEventListener("load", function(aEvent) {
|
||||
newWin.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
newWin.gBrowser.selectedBrowser.addEventListener("load", function () {
|
||||
// Good enough for checking the state
|
||||
afterTestCallback(previousClosedWindowData, ss.getClosedWindowData());
|
||||
afterTestCleanup(newWin);
|
||||
}, true);
|
||||
|
||||
}, false);
|
||||
}
|
||||
|
||||
function afterTestCleanup(aNewWin) {
|
||||
executeSoon(function() {
|
||||
aNewWin.close();
|
||||
document.documentElement.setAttribute("windowtype", originalWindowType);
|
||||
runNextTestOrFinish();
|
||||
});
|
||||
}
|
||||
|
@ -385,8 +385,6 @@
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
@BINPATH@/components/SyncComponents.manifest
|
||||
@BINPATH@/components/Weave.js
|
||||
@BINPATH@/components/WeaveCrypto.manifest
|
||||
@BINPATH@/components/WeaveCrypto.js
|
||||
#endif
|
||||
|
||||
; Modules
|
||||
|
@ -67,6 +67,8 @@ components/nsUrlClassifierTable.js
|
||||
components/nsXmlRpcClient.js
|
||||
components/pluginGlue.js
|
||||
components/sidebar.xpt
|
||||
components/WeaveCrypto.js
|
||||
components/WeaveCrypto.manifest
|
||||
components/xmlextras.xpt
|
||||
components/xpcom.xpt
|
||||
components/xpti.dat
|
||||
@ -621,7 +623,6 @@ xpicleanup@BIN_SUFFIX@
|
||||
components/storage-Legacy.js
|
||||
components/storage-mozStorage.js
|
||||
components/txEXSLTRegExFunctions.js
|
||||
components/WeaveCrypto.js
|
||||
components/Weave.js
|
||||
components/WebContentConverter.js
|
||||
defaults/autoconfig/platform.js
|
||||
|
@ -33,19 +33,12 @@
|
||||
0 0 2px 1px rgba(255,255,255,.25) inset;
|
||||
}
|
||||
|
||||
#main-window[privatebrowsingmode=temporary] #appmenu-button:not(:-moz-window-inactive) {
|
||||
#main-window[privatebrowsingmode=temporary] #appmenu-button {
|
||||
-moz-border-left-colors: rgba(255,255,255,.5) rgba(43,8,65,.9);
|
||||
-moz-border-bottom-colors: rgba(255,255,255,.5) rgba(43,8,65,.9);
|
||||
-moz-border-right-colors: rgba(255,255,255,.5) rgba(43,8,65,.9);
|
||||
}
|
||||
|
||||
#appmenu-button:-moz-window-inactive {
|
||||
-moz-border-left-colors: rgba(255,255,255,.4) rgba(0,0,0,.5);
|
||||
-moz-border-bottom-colors: rgba(255,255,255,.4) rgba(0,0,0,.5);
|
||||
-moz-border-right-colors: rgba(255,255,255,.4) rgba(0,0,0,.5);
|
||||
box-shadow: 0 0 0 1px rgba(255,255,255,.25) inset;
|
||||
}
|
||||
|
||||
/* Bug 413060, comment 16: Vista Aero is a special case where we use a
|
||||
tooltip appearance for the address bar popup panels */
|
||||
#identity-popup,
|
||||
|
@ -126,16 +126,11 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#main-window[privatebrowsingmode=temporary] #appmenu-button:not(:-moz-window-inactive) {
|
||||
#main-window[privatebrowsingmode=temporary] #appmenu-button {
|
||||
background-image: -moz-linear-gradient(rgb(153,38,211), rgb(105,19,163) 95%);
|
||||
border-color: rgba(43,8,65,.9);
|
||||
}
|
||||
|
||||
#appmenu-button:-moz-window-inactive {
|
||||
background-image: none;
|
||||
border-color: rgba(0,0,0,.4);
|
||||
}
|
||||
|
||||
#appmenu-button:hover:not(:active):not([open]) {
|
||||
background-image: -moz-radial-gradient(center bottom, farthest-side, rgba(252,240,89,.5) 10%, rgba(252,240,89,0) 70%),
|
||||
-moz-radial-gradient(center bottom, farthest-side, rgb(236,133,0), rgba(255,229,172,0)),
|
||||
|
@ -457,7 +457,7 @@ EnumerateOverride(nsIURI* aURIKey,
|
||||
|
||||
struct EnumerationArgs
|
||||
{
|
||||
nsTArray<ChromePackage>& packages;
|
||||
InfallibleTArray<ChromePackage>& packages;
|
||||
const nsCString& selectedLocale;
|
||||
const nsCString& selectedSkin;
|
||||
};
|
||||
@ -466,9 +466,9 @@ void
|
||||
nsChromeRegistryChrome::SendRegisteredChrome(
|
||||
mozilla::dom::PContentParent* aParent)
|
||||
{
|
||||
nsTArray<ChromePackage> packages;
|
||||
nsTArray<ResourceMapping> resources;
|
||||
nsTArray<OverrideMapping> overrides;
|
||||
InfallibleTArray<ChromePackage> packages;
|
||||
InfallibleTArray<ResourceMapping> resources;
|
||||
InfallibleTArray<OverrideMapping> overrides;
|
||||
|
||||
EnumerationArgs args = {
|
||||
packages, mSelectedLocale, mSelectedSkin
|
||||
|
@ -612,7 +612,6 @@ AIX_OBJMODEL = @AIX_OBJMODEL@
|
||||
|
||||
# For OS/2 build
|
||||
MOZ_OS2_TOOLS = @MOZ_OS2_TOOLS@
|
||||
MOZ_OS2_USE_DECLSPEC = @MOZ_OS2_USE_DECLSPEC@
|
||||
MOZ_OS2_HIGH_MEMORY = @MOZ_OS2_HIGH_MEMORY@
|
||||
|
||||
HAVE_XIE=@HAVE_XIE@
|
||||
|
@ -1249,26 +1249,10 @@ $(DEF_FILE): $(OBJS) $(SHARED_LIBRARY_LIBS)
|
||||
echo CODE LOADONCALL MOVEABLE DISCARDABLE >> $@
|
||||
echo DATA PRELOAD MOVEABLE MULTIPLE NONSHARED >> $@
|
||||
echo EXPORTS >> $@
|
||||
ifeq ($(IS_COMPONENT),1)
|
||||
ifeq ($(HAS_EXTRAEXPORTS),1)
|
||||
ifndef MOZ_OS2_USE_DECLSPEC
|
||||
$(FILTER) $(OBJS) $(SHARED_LIBRARY_LIBS) >> $@
|
||||
endif
|
||||
else
|
||||
echo _NSModule >> $@
|
||||
endif
|
||||
else
|
||||
ifndef MOZ_OS2_USE_DECLSPEC
|
||||
$(FILTER) $(OBJS) $(SHARED_LIBRARY_LIBS) >> $@
|
||||
endif
|
||||
endif
|
||||
|
||||
$(ADD_TO_DEF_FILE)
|
||||
|
||||
ifdef MOZ_OS2_USE_DECLSPEC
|
||||
$(IMPORT_LIBRARY): $(SHARED_LIBRARY)
|
||||
else
|
||||
$(IMPORT_LIBRARY): $(DEF_FILE)
|
||||
endif
|
||||
rm -f $@
|
||||
$(IMPLIB) $@ $^
|
||||
$(RANLIB) $@
|
||||
|
@ -2642,7 +2642,7 @@ ia64*-hpux*)
|
||||
DSO_LDOPTS='-Zdll'
|
||||
BIN_FLAGS='-Zlinker /ST:0x100000'
|
||||
IMPLIB='emximp -o'
|
||||
FILTER='emxexp -o'
|
||||
FILTER='true'
|
||||
LDFLAGS='-Zmap'
|
||||
WARNINGS_AS_ERRORS='-Werror'
|
||||
MOZ_DEBUG_FLAGS="-g -fno-inline"
|
||||
@ -2678,9 +2678,8 @@ ia64*-hpux*)
|
||||
[return 0;],
|
||||
ac_os2_declspec="yes",
|
||||
ac_os2_declspec="no")])
|
||||
if test "$ac_os2_declspec" = "yes"; then
|
||||
FILTER='true'
|
||||
MOZ_OS2_USE_DECLSPEC='1'
|
||||
if test "$ac_os2_declspec" != "yes"; then
|
||||
AC_MSG_ERROR([Compiler does not support __declspec(dllexport), install GCC-4.3.2 or newer])
|
||||
fi
|
||||
;;
|
||||
|
||||
@ -8934,7 +8933,6 @@ AC_SUBST(USE_DEPENDENT_LIBS)
|
||||
|
||||
AC_SUBST(MOZ_BUILD_ROOT)
|
||||
AC_SUBST(MOZ_OS2_TOOLS)
|
||||
AC_SUBST(MOZ_OS2_USE_DECLSPEC)
|
||||
|
||||
AC_SUBST(MOZ_POST_DSO_LIB_COMMAND)
|
||||
AC_SUBST(MOZ_POST_PROGRAM_COMMAND)
|
||||
|
@ -1700,6 +1700,14 @@ public:
|
||||
*/
|
||||
static bool IsSubDocumentTabbable(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Flushes the layout tree (recursively)
|
||||
*
|
||||
* @param aWindow the window the flush should start at
|
||||
*
|
||||
*/
|
||||
static void FlushLayoutForTree(nsIDOMWindow* aWindow);
|
||||
|
||||
private:
|
||||
|
||||
static PRBool InitializeEventTable();
|
||||
|
@ -738,7 +738,7 @@ public:
|
||||
nsSlots* s = GetSlots();
|
||||
if (s) {
|
||||
NS_ASSERTION(s->mMutationObservers.IndexOf(aMutationObserver) ==
|
||||
nsTArray_base::NoIndex,
|
||||
nsTArray<int>::NoIndex,
|
||||
"Observer already in the list");
|
||||
s->mMutationObservers.AppendElement(aMutationObserver);
|
||||
}
|
||||
|
@ -58,8 +58,9 @@ class nsIAtom;
|
||||
class nsICSSStyleRule;
|
||||
class nsISVGValue;
|
||||
class nsIDocument;
|
||||
template<class E> class nsTArray;
|
||||
template<class E> class nsTPtrArray;
|
||||
template<class E, class A> class nsTArray;
|
||||
template<class E, class A> class nsTPtrArray;
|
||||
struct nsTArrayDefaultAllocator;
|
||||
|
||||
#define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
|
||||
|
||||
@ -381,7 +382,7 @@ private:
|
||||
PRBool aCanBePercent = PR_FALSE,
|
||||
PRBool* aIsPercent = nsnull) const;
|
||||
|
||||
static nsTPtrArray<const EnumTable>* sEnumTableArray;
|
||||
static nsTPtrArray<const EnumTable, nsTArrayDefaultAllocator>* sEnumTableArray;
|
||||
|
||||
PtrBits mBits;
|
||||
};
|
||||
|
@ -6303,6 +6303,40 @@ nsContentUtils::IsSubDocumentTabbable(nsIContent* aContent)
|
||||
return !zombieViewer;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::FlushLayoutForTree(nsIDOMWindow* aWindow)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
|
||||
if (!piWin)
|
||||
return;
|
||||
|
||||
// Note that because FlushPendingNotifications flushes parents, this
|
||||
// is O(N^2) in docshell tree depth. However, the docshell tree is
|
||||
// usually pretty shallow.
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
aWindow->GetDocument(getter_AddRefs(domDoc));
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||
if (doc) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeNode> node =
|
||||
do_QueryInterface(piWin->GetDocShell());
|
||||
if (node) {
|
||||
PRInt32 i = 0, i_end;
|
||||
node->GetChildCount(&i_end);
|
||||
for (; i < i_end; ++i) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> item;
|
||||
node->GetChildAt(i, getter_AddRefs(item));
|
||||
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
|
||||
if (win) {
|
||||
FlushLayoutForTree(win);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nsContentUtils::RemoveNewlines(nsString &aString)
|
||||
{
|
||||
// strip CR/LF and null
|
||||
|
@ -205,6 +205,8 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
typedef nsTArray<Link*> LinkArray;
|
||||
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static PRLogModuleInfo* gDocumentLeakPRLog;
|
||||
@ -3902,7 +3904,7 @@ nsDocument::InternalAllowXULXBL()
|
||||
void
|
||||
nsDocument::AddObserver(nsIDocumentObserver* aObserver)
|
||||
{
|
||||
NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray_base::NoIndex,
|
||||
NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
|
||||
"Observer already in the list");
|
||||
mObservers.AppendElement(aObserver);
|
||||
AddMutationObserver(aObserver);
|
||||
@ -5543,8 +5545,10 @@ nsDocument::GetAnimationController()
|
||||
}
|
||||
}
|
||||
|
||||
// If we're hidden (or being hidden), notify the animation controller.
|
||||
if (!mIsShowing) {
|
||||
// If we're hidden (or being hidden), notify the newly-created animation
|
||||
// controller. (Skip this check for SVG-as-an-image documents, though,
|
||||
// because they don't get OnPageShow / OnPageHide calls).
|
||||
if (!mIsShowing && !mIsBeingUsedAsImage) {
|
||||
mAnimationController->OnPageHide();
|
||||
}
|
||||
|
||||
@ -7546,7 +7550,7 @@ static
|
||||
PLDHashOperator
|
||||
EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
|
||||
{
|
||||
nsTArray<Link*>* array = static_cast<nsTArray<Link*>*>(aArray);
|
||||
LinkArray* array = static_cast<LinkArray*>(aArray);
|
||||
(void)array->AppendElement(aEntry->GetKey());
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
@ -7557,12 +7561,12 @@ nsDocument::RefreshLinkHrefs()
|
||||
// Get a list of all links we know about. We will reset them, which will
|
||||
// remove them from the document, so we need a copy of what is in the
|
||||
// hashtable.
|
||||
nsTArray<Link*> linksToNotify(mStyledLinks.Count());
|
||||
LinkArray linksToNotify(mStyledLinks.Count());
|
||||
(void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
|
||||
|
||||
// Reset all of our styled links.
|
||||
MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_STATE, PR_TRUE);
|
||||
for (nsTArray_base::size_type i = 0; i < linksToNotify.Length(); i++) {
|
||||
for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
|
||||
linksToNotify[i]->ResetLinkState(true);
|
||||
}
|
||||
}
|
||||
|
@ -723,8 +723,7 @@ nsFrameLoader::ShowRemoteFrame(const nsIntSize& size)
|
||||
mRemoteBrowser->Show(size);
|
||||
mRemoteBrowserShown = PR_TRUE;
|
||||
|
||||
nsCOMPtr<nsIChromeFrameMessageManager> dummy;
|
||||
GetMessageManager(getter_AddRefs(dummy)); // Initialize message manager.
|
||||
EnsureMessageManager();
|
||||
} else {
|
||||
mRemoteBrowser->Move(size);
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ nsFrameMessageManager::SendSyncMessage()
|
||||
nsString json;
|
||||
nsresult rv = GetParamsForMessage(messageName, json);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsTArray<nsString> retval;
|
||||
InfallibleTArray<nsString> retval;
|
||||
if (mSyncCallback(mCallbackData, messageName, json, &retval)) {
|
||||
nsAXPCNativeCallContext* ncc = nsnull;
|
||||
rv = nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc);
|
||||
@ -326,7 +326,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||
const nsAString& aMessage,
|
||||
PRBool aSync, const nsAString& aJSON,
|
||||
JSObject* aObjectsArray,
|
||||
nsTArray<nsString>* aJSONRetVal,
|
||||
InfallibleTArray<nsString>* aJSONRetVal,
|
||||
JSContext* aContext)
|
||||
{
|
||||
JSContext* ctx = mContext ? mContext : aContext;
|
||||
@ -742,7 +742,7 @@ bool SendAsyncMessageToChildProcess(void* aCallbackData,
|
||||
bool SendSyncMessageToParentProcess(void* aCallbackData,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aJSON,
|
||||
nsTArray<nsString>* aJSONRetVal)
|
||||
InfallibleTArray<nsString>* aJSONRetVal)
|
||||
{
|
||||
mozilla::dom::ContentChild* cc =
|
||||
mozilla::dom::ContentChild::GetSingleton();
|
||||
|
@ -66,7 +66,7 @@ typedef bool (*nsLoadScriptCallback)(void* aCallbackData, const nsAString& aURL)
|
||||
typedef bool (*nsSyncMessageCallback)(void* aCallbackData,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aJSON,
|
||||
nsTArray<nsString>* aJSONRetVal);
|
||||
InfallibleTArray<nsString>* aJSONRetVal);
|
||||
typedef bool (*nsAsyncMessageCallback)(void* aCallbackData,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aJSON);
|
||||
@ -129,7 +129,7 @@ public:
|
||||
nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
|
||||
PRBool aSync, const nsAString& aJSON,
|
||||
JSObject* aObjectsArray,
|
||||
nsTArray<nsString>* aJSONRetVal,
|
||||
InfallibleTArray<nsString>* aJSONRetVal,
|
||||
JSContext* aContext = nsnull);
|
||||
void AddChildManager(nsFrameMessageManager* aManager,
|
||||
PRBool aLoadScripts = PR_TRUE);
|
||||
|
@ -54,7 +54,7 @@
|
||||
bool SendSyncMessageToParent(void* aCallbackData,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aJSON,
|
||||
nsTArray<nsString>* aJSONRetVal)
|
||||
InfallibleTArray<nsString>* aJSONRetVal)
|
||||
{
|
||||
nsInProcessTabChildGlobal* tabChild =
|
||||
static_cast<nsInProcessTabChildGlobal*>(aCallbackData);
|
||||
|
@ -15,7 +15,7 @@
|
||||
* The Original Code is Mozilla Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2006
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
@ -146,29 +146,6 @@ nsScriptElement::ContentInserted(nsIDocument *aDocument,
|
||||
MaybeProcessScript();
|
||||
}
|
||||
|
||||
static PRBool
|
||||
InNonScriptingContainer(nsIContent* aNode)
|
||||
{
|
||||
aNode = aNode->GetParent();
|
||||
while (aNode) {
|
||||
// XXX noframes and noembed are currently unconditionally not
|
||||
// displayed and processed. This might change if we support either
|
||||
// prefs or per-document container settings for not allowing
|
||||
// frames or plugins.
|
||||
if (aNode->IsHTML()) {
|
||||
nsIAtom *localName = aNode->Tag();
|
||||
if (localName == nsGkAtoms::iframe ||
|
||||
localName == nsGkAtoms::noframes ||
|
||||
localName == nsGkAtoms::noembed) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
aNode = aNode->GetParent();
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptElement::MaybeProcessScript()
|
||||
{
|
||||
@ -185,16 +162,9 @@ nsScriptElement::MaybeProcessScript()
|
||||
|
||||
FreezeUriAsyncDefer();
|
||||
|
||||
if (InNonScriptingContainer(cont)) {
|
||||
// Make sure to flag ourselves as evaluated
|
||||
mAlreadyStarted = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult scriptresult = NS_OK;
|
||||
nsRefPtr<nsScriptLoader> loader = cont->GetOwnerDoc()->ScriptLoader();
|
||||
mAlreadyStarted = PR_TRUE;
|
||||
scriptresult = loader->ProcessScriptElement(this);
|
||||
nsresult scriptresult = loader->ProcessScriptElement(this);
|
||||
|
||||
// The only error we don't ignore is NS_ERROR_HTMLPARSER_BLOCK
|
||||
// However we don't want to override other success values
|
||||
|
@ -13,11 +13,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=453736
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=453736">Mozilla Bug 453736</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 453736 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
@ -26,39 +24,7 @@ addLoadEvent(function() {
|
||||
function() { return document.createElementNS("http://www.w3.org/2000/svg", "script"); }
|
||||
];
|
||||
|
||||
const noScriptContainers = ["iframe", "noframes", "noembed"];
|
||||
for (var i = 0; i < noScriptContainers.length; ++i) {
|
||||
for each (var func in scriptCreationFuncs) {
|
||||
var cont = noScriptContainers[i];
|
||||
var node = document.createElement(cont);
|
||||
document.body.appendChild(node);
|
||||
var s = func();
|
||||
s.setAttribute("type", "application/javascript");
|
||||
s.appendChild(document.createTextNode('window["'+cont+'ScriptRan"] = true'));
|
||||
|
||||
window[cont+"ScriptRan"] = false;
|
||||
document.body.appendChild(s.cloneNode(true));
|
||||
is(window[cont+"ScriptRan"], true,
|
||||
"Clone of non-inserted script created with " + func +" should run");
|
||||
|
||||
window[cont+"ScriptRan"] = false;
|
||||
node.appendChild(s);
|
||||
is(window[cont+"ScriptRan"], false,
|
||||
"Script created with " + func +" shouldn't run when inserting in <"+cont+">");
|
||||
|
||||
window[cont+"ScriptRan"] = false;
|
||||
document.body.appendChild(s);
|
||||
is(window[cont+"ScriptRan"], false,
|
||||
"Script created with " + func + " shouldn't run when moving out of <"+cont+">");
|
||||
|
||||
window[cont+"ScriptRan"] = false;
|
||||
document.body.appendChild(s.cloneNode(true));
|
||||
is(window[cont+"ScriptRan"], false,
|
||||
"Clone of script inside <" + cont + "> created with " + func + " shouldn't run");
|
||||
}
|
||||
}
|
||||
|
||||
const scriptContainers = ["div"];
|
||||
const scriptContainers = ["div", "iframe", "noframes", "noembed"];
|
||||
for (var i = 0; i < scriptContainers.length; ++i) {
|
||||
for each (var func in scriptCreationFuncs) {
|
||||
var cont = scriptContainers[i];
|
||||
@ -87,10 +53,6 @@ addLoadEvent(function() {
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
@ -64,40 +64,6 @@ DocumentRendererChild::DocumentRendererChild()
|
||||
DocumentRendererChild::~DocumentRendererChild()
|
||||
{}
|
||||
|
||||
static void
|
||||
FlushLayoutForTree(nsIDOMWindow* aWindow)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
|
||||
if (!piWin)
|
||||
return;
|
||||
|
||||
// Note that because FlushPendingNotifications flushes parents, this
|
||||
// is O(N^2) in docshell tree depth. However, the docshell tree is
|
||||
// usually pretty shallow.
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
aWindow->GetDocument(getter_AddRefs(domDoc));
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||
if (doc) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeNode> node =
|
||||
do_QueryInterface(piWin->GetDocShell());
|
||||
if (node) {
|
||||
PRInt32 i = 0, i_end;
|
||||
node->GetChildCount(&i_end);
|
||||
for (; i < i_end; ++i) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> item;
|
||||
node->GetChildAt(i, getter_AddRefs(item));
|
||||
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
|
||||
if (win) {
|
||||
FlushLayoutForTree(win);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DocumentRendererChild::RenderDocument(nsIDOMWindow *window,
|
||||
const nsRect& documentRect,
|
||||
@ -109,7 +75,7 @@ DocumentRendererChild::RenderDocument(nsIDOMWindow *window,
|
||||
nsCString& data)
|
||||
{
|
||||
if (flushLayout)
|
||||
FlushLayoutForTree(window);
|
||||
nsContentUtils::FlushLayoutForTree(window);
|
||||
|
||||
nsCOMPtr<nsPresContext> presContext;
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(window);
|
||||
|
@ -3562,41 +3562,6 @@ nsCanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
FlushLayoutForTree(nsIDOMWindow* aWindow)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
|
||||
if (!piWin)
|
||||
return;
|
||||
|
||||
// Note that because FlushPendingNotifications flushes parents, this
|
||||
// is O(N^2) in docshell tree depth. However, the docshell tree is
|
||||
// usually pretty shallow.
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
aWindow->GetDocument(getter_AddRefs(domDoc));
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||
if (doc) {
|
||||
doc->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeNode> node =
|
||||
do_QueryInterface(piWin->GetDocShell());
|
||||
if (node) {
|
||||
PRInt32 i = 0, i_end;
|
||||
node->GetChildCount(&i_end);
|
||||
for (; i < i_end; ++i) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> item;
|
||||
node->GetChildAt(i, getter_AddRefs(item));
|
||||
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(item);
|
||||
if (win) {
|
||||
FlushLayoutForTree(win);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY,
|
||||
float aW, float aH,
|
||||
@ -3625,7 +3590,7 @@ nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY
|
||||
|
||||
// Flush layout updates
|
||||
if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH))
|
||||
FlushLayoutForTree(aWindow);
|
||||
nsContentUtils::FlushLayoutForTree(aWindow);
|
||||
|
||||
nsRefPtr<nsPresContext> presContext;
|
||||
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aWindow);
|
||||
|
@ -373,8 +373,7 @@ nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId,
|
||||
{
|
||||
NS_ENSURE_ARG(aContext);
|
||||
|
||||
nsCString ctxId;
|
||||
ctxId.Assign(NS_LossyConvertUTF16toASCII(aContextId));
|
||||
NS_LossyConvertUTF16toASCII ctxId(aContextId);
|
||||
|
||||
// check that ctxId is clamped to A-Za-z0-9_-
|
||||
for (PRUint32 i = 0; i < ctxId.Length(); i++) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Pierre Phaneuf <pp@ludusdesign.com>
|
||||
* Geoff Lankow <geoff@darktrojan.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
@ -540,6 +541,9 @@ UploadLastDir::StoreLastUsedDirectory(nsIURI* aURI, nsILocalFile* aFile)
|
||||
NS_PRECONDITION(aFile, "aFile is null");
|
||||
nsCOMPtr<nsIFile> parentFile;
|
||||
aFile->GetParent(getter_AddRefs(parentFile));
|
||||
if (!parentFile) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(parentFile);
|
||||
|
||||
// Store the data in memory instead of the CPS during private browsing mode
|
||||
|
@ -1217,8 +1217,7 @@ NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Autofocus, autofocus)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Disabled, disabled)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Multiple, multiple)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLSelectElement, Name, name)
|
||||
NS_IMPL_POSITIVE_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size,
|
||||
GetDefaultSize())
|
||||
NS_IMPL_POSITIVE_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size, 0)
|
||||
NS_IMPL_INT_ATTR(nsHTMLSelectElement, TabIndex, tabindex)
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -495,14 +495,6 @@ protected:
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the default size.
|
||||
*/
|
||||
PRInt32 GetDefaultSize() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ? 4 : 1;
|
||||
}
|
||||
|
||||
/** The options[] array */
|
||||
nsRefPtr<nsHTMLOptionCollection> mOptions;
|
||||
/** false if the parser is in the middle of adding children. */
|
||||
|
@ -240,6 +240,7 @@ _TEST_FILES = \
|
||||
file_bug297761.html \
|
||||
test_bug607145.html \
|
||||
test_bug601061.html \
|
||||
reflect.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
95
content/html/content/test/reflect.js
Normal file
95
content/html/content/test/reflect.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Checks that a given attribute name for a given element is correctly reflected
|
||||
* as an unsigned int.
|
||||
*/
|
||||
function reflectUnsignedInt(aElement, aAttr, aNonNull, aDefault)
|
||||
{
|
||||
function checkGetter(aElement, aAttr, aValue)
|
||||
{
|
||||
is(aElement[aAttr], aValue, "." + aAttr + " should be equals " + aValue);
|
||||
is(aElement.getAttribute(aAttr), aValue,
|
||||
"@" + aAttr + " should be equals " + aValue);
|
||||
}
|
||||
|
||||
if (!aDefault) {
|
||||
if (aNonNull) {
|
||||
aDefault = 1;
|
||||
} else {
|
||||
aDefault = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check default value.
|
||||
is(aElement[aAttr], aDefault, "default value should be " + aDefault);
|
||||
ok(!aElement.hasAttribute(aAttr), aAttr + " shouldn't be present");
|
||||
|
||||
var values = [ 1, 3, 42, 2147483647 ];
|
||||
|
||||
for each (var value in values) {
|
||||
aElement[aAttr] = value;
|
||||
checkGetter(aElement, aAttr, value);
|
||||
}
|
||||
|
||||
for each (var value in values) {
|
||||
aElement.setAttribute(aAttr, value);
|
||||
checkGetter(aElement, aAttr, value);
|
||||
}
|
||||
|
||||
// -3000000000 is equivalent to 1294967296 when using the IDL attribute.
|
||||
aElement[aAttr] = -3000000000;
|
||||
checkGetter(aElement, aAttr, 1294967296);
|
||||
// When setting the content atribute, it's a string so it will be unvalid.
|
||||
aElement.setAttribute(aAttr, -3000000000);
|
||||
is(aElement.getAttribute(aAttr), -3000000000,
|
||||
"@" + aAttr + " should be equals to " + -3000000000);
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
|
||||
var nonValidValues = [
|
||||
/* invalid value, value in the unsigned int range */
|
||||
[ -2147483648, 2147483648 ],
|
||||
[ -1, 4294967295 ],
|
||||
[ 3147483647, 3147483647 ],
|
||||
];
|
||||
|
||||
for each (var values in nonValidValues) {
|
||||
aElement[aAttr] = values[0];
|
||||
is(aElement.getAttribute(aAttr), values[1],
|
||||
"@" + aAttr + " should be equals to " + values[1]);
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
}
|
||||
|
||||
for each (var values in nonValidValues) {
|
||||
aElement.setAttribute(aAttr, values[0]);
|
||||
is(aElement.getAttribute(aAttr), values[0],
|
||||
"@" + aAttr + " should be equals to " + values[0]);
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
}
|
||||
|
||||
// Setting to 0 should throw an error if aNonNull is true.
|
||||
var caught = false;
|
||||
try {
|
||||
aElement[aAttr] = 0;
|
||||
} catch(e) {
|
||||
caught = true;
|
||||
is(e.code, DOMException.INDEX_SIZE_ERR, "exception should be INDEX_SIZE_ERR");
|
||||
}
|
||||
|
||||
if (aNonNull) {
|
||||
ok(caught, "an exception should have been caught");
|
||||
} else {
|
||||
ok(!caught, "no exception should have been caught");
|
||||
}
|
||||
|
||||
// If 0 is set in @aAttr, it will be ignored when calling .aAttr.
|
||||
aElement.setAttribute(aAttr, 0);
|
||||
is(aElement.getAttribute(aAttr), 0, "@" + aAttr + " should be equals to 0");
|
||||
if (aNonNull) {
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
} else {
|
||||
is(aElement[aAttr], 0, "." + aAttr + " should be equals to 0");
|
||||
}
|
||||
}
|
@ -122,36 +122,36 @@ function checkSetSizeException(element)
|
||||
element.removeAttribute('size');
|
||||
}
|
||||
|
||||
function checkSizeWhenChangeMultiple(element)
|
||||
function checkSizeWhenChangeMultiple(element, aDefaultNonMultiple, aDefaultMultiple)
|
||||
{
|
||||
s.setAttribute('size', -1)
|
||||
is(s.size, 1, "Size IDL attribute should be 1");
|
||||
is(s.size, aDefaultNonMultiple, "Size IDL attribute should be 1");
|
||||
|
||||
s.multiple = true;
|
||||
is(s.size, 4, "Size IDL attribute should be 4");
|
||||
is(s.size, aDefaultMultiple, "Size IDL attribute should be 4");
|
||||
|
||||
is(s.getAttribute('size'), -1, "Size content attribute should be -1");
|
||||
|
||||
s.setAttribute('size', -2);
|
||||
is(s.size, 4, "Size IDL attribute should be 4");
|
||||
is(s.size, aDefaultMultiple, "Size IDL attribute should be 4");
|
||||
|
||||
s.multiple = false;
|
||||
is(s.size, 1, "Size IDL attribute should be 1");
|
||||
is(s.size, aDefaultNonMultiple, "Size IDL attribute should be 1");
|
||||
|
||||
is(s.getAttribute('size'), -2, "Size content attribute should be -2");
|
||||
}
|
||||
|
||||
var s = document.getElementById('s');
|
||||
|
||||
checkSizeReflection(s, 1);
|
||||
checkSizeReflection(s, 0);
|
||||
checkSetSizeException(s);
|
||||
|
||||
s.setAttribute('multiple', 'true');
|
||||
checkSizeReflection(s, 4);
|
||||
checkSizeReflection(s, 0);
|
||||
checkSetSizeException(s);
|
||||
s.removeAttribute('multiple');
|
||||
|
||||
checkSizeWhenChangeMultiple(s);
|
||||
checkSizeWhenChangeMultiple(s, 0, 0);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=601061
|
||||
<title>Test for Bug 601061</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="reflect.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
@ -17,100 +18,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=601061
|
||||
|
||||
/** Test for Bug 601061 **/
|
||||
|
||||
function reflectUnsignedInt(aElement, aAttr, aNonNull, aDefault)
|
||||
{
|
||||
function checkGetter(aElement, aAttr, aValue)
|
||||
{
|
||||
is(aElement[aAttr], aValue, "." + aAttr + " should be equals " + aValue);
|
||||
is(aElement.getAttribute(aAttr), aValue,
|
||||
"@" + aAttr + " should be equals " + aValue);
|
||||
}
|
||||
|
||||
if (!aDefault) {
|
||||
if (aNonNull) {
|
||||
aDefault = 1;
|
||||
} else {
|
||||
aDefault = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check default value.
|
||||
is(aElement[aAttr], aDefault, "default value should be " + aDefault);
|
||||
ok(!aElement.hasAttribute(aAttr), aAttr + " shouldn't be present");
|
||||
|
||||
var values = [ 1, 3, 42, 2147483647 ];
|
||||
|
||||
for each (var value in values) {
|
||||
aElement[aAttr] = value;
|
||||
checkGetter(aElement, aAttr, value);
|
||||
}
|
||||
|
||||
for each (var value in values) {
|
||||
aElement.setAttribute(aAttr, value);
|
||||
checkGetter(aElement, aAttr, value);
|
||||
}
|
||||
|
||||
// -3000000000 is equivalent to 1294967296 when using the IDL attribute.
|
||||
aElement[aAttr] = -3000000000;
|
||||
checkGetter(aElement, aAttr, 1294967296);
|
||||
// When setting the content atribute, it's a string so it will be unvalid.
|
||||
aElement.setAttribute(aAttr, -3000000000);
|
||||
is(aElement.getAttribute(aAttr), -3000000000,
|
||||
"@" + aAttr + " should be equals to " + -3000000000);
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
|
||||
var nonValidValues = [
|
||||
/* invalid value, value in the unsigned int range */
|
||||
[ -2147483648, 2147483648 ],
|
||||
[ -1, 4294967295 ],
|
||||
[ 3147483647, 3147483647 ],
|
||||
];
|
||||
|
||||
for each (var values in nonValidValues) {
|
||||
aElement[aAttr] = values[0];
|
||||
is(aElement.getAttribute(aAttr), values[1],
|
||||
"@" + aAttr + " should be equals to " + values[1]);
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
}
|
||||
|
||||
for each (var values in nonValidValues) {
|
||||
aElement.setAttribute(aAttr, values[0]);
|
||||
is(aElement.getAttribute(aAttr), values[0],
|
||||
"@" + aAttr + " should be equals to " + values[0]);
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
}
|
||||
|
||||
// Setting to 0 should throw an error if aNonNull is true.
|
||||
var caught = false;
|
||||
try {
|
||||
aElement[aAttr] = 0;
|
||||
} catch(e) {
|
||||
caught = true;
|
||||
is(e.code, DOMException.INDEX_SIZE_ERR, "exception should be INDEX_SIZE_ERR");
|
||||
}
|
||||
|
||||
if (aNonNull) {
|
||||
ok(caught, "an exception should have been caught");
|
||||
} else {
|
||||
ok(!caught, "no exception should have been caught");
|
||||
}
|
||||
|
||||
// If 0 is set in @aAttr, it will be ignored when calling .aAttr.
|
||||
aElement.setAttribute(aAttr, 0);
|
||||
is(aElement.getAttribute(aAttr), 0, "@" + aAttr + " should be equals to 0");
|
||||
if (aNonNull) {
|
||||
is(aElement[aAttr], aDefault,
|
||||
"." + aAttr + " should be equals to " + aDefault);
|
||||
} else {
|
||||
is(aElement[aAttr], 0, "." + aAttr + " should be equals to 0");
|
||||
}
|
||||
}
|
||||
|
||||
var input = document.createElement("input");
|
||||
reflectUnsignedInt(input, "size", true, 20);
|
||||
reflectUnsignedInt(document.createElement("input"), "size", true, 20);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -20,11 +20,15 @@ var manager = new MediaTestManager;
|
||||
|
||||
function do_loadedmetadata(e) {
|
||||
var v = e.target;
|
||||
if (e._finished)
|
||||
if (v._finished)
|
||||
return false;
|
||||
|
||||
v._seekTime = Math.round(v.duration / 2);
|
||||
v.currentTime=v._seekTime;
|
||||
var duration = v.duration;
|
||||
v._seekTime = Math.round(duration / 2);
|
||||
ok(!isNaN(duration), v._name + ": duration must be non NaN");
|
||||
ok(v._seekTime >= 0.0, v._name + ": must have non-negative seek target");
|
||||
ok(!isNaN(v._seekTime), v._name + ": seek target must be non NaN");
|
||||
v.currentTime = v._seekTime;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -35,9 +39,8 @@ function do_seeking(e) {
|
||||
|
||||
v._seeking = true;
|
||||
ok(v.currentTime == v._seekTime,
|
||||
"Check currentTime of " + v.currentTime +
|
||||
" is requested seek time of " + v._seekTime +
|
||||
" in " + v._name);
|
||||
v._name + ": currentTime of " + v.currentTime +
|
||||
" should be requested seek time of " + v._seekTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -47,12 +50,13 @@ function do_timeupdate(e) {
|
||||
return false;
|
||||
|
||||
ok(v.currentTime == v._seekTime,
|
||||
"Check currentTime of " + v.currentTime +
|
||||
" is requested seek time of " + v._seekTime +
|
||||
" in " + v._name);
|
||||
v._finished= true;
|
||||
v._name + ": currentTime of " + v.currentTime +
|
||||
" should be requested seek time of " + v._seekTime);
|
||||
v._finished = true;
|
||||
v.pause();
|
||||
v.parentNode.removeChild(v);
|
||||
v.src = "";
|
||||
ok(true, v._name + ": finished");
|
||||
manager.finished(v.token);
|
||||
return false;
|
||||
}
|
||||
@ -63,6 +67,7 @@ function startTest(test, token) {
|
||||
v.token = token;
|
||||
manager.started(token);
|
||||
v.src = test.name;
|
||||
ok(true, test.name + ": started");
|
||||
v._name = test.name;
|
||||
v._seeking = false;
|
||||
v._finished = false;
|
||||
|
12
content/smil/crashtests/572938-1.svg
Normal file
12
content/smil/crashtests/572938-1.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<text id="myText">Used Text Element
|
||||
<set attributeName="display" to="none"/>
|
||||
</text>
|
||||
</defs>
|
||||
<use xlink:href="#myText" x="20" y="40"/>
|
||||
<text x="20" y="60">Normal Text Element
|
||||
<set attributeName="display" to="none"/>
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 352 B |
22
content/smil/crashtests/572938-2.svg
Normal file
22
content/smil/crashtests/572938-2.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="reftest-wait">
|
||||
|
||||
<script>
|
||||
function boom()
|
||||
{
|
||||
document.getElementById("circleID").removeChild(
|
||||
document.getElementById("at"));
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
window.addEventListener("load", boom, false);
|
||||
</script>
|
||||
|
||||
<circle id="circleID">
|
||||
<animate/>
|
||||
<animateTransform id="at" attributeName="transform"/>
|
||||
</circle>
|
||||
<animate attributeName="stroke-width"/>
|
||||
<use xlink:href="#circleID"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 580 B |
10
content/smil/crashtests/572938-3.svg
Normal file
10
content/smil/crashtests/572938-3.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<text id="a">Text A</text>
|
||||
<text id="b">Text B</text>
|
||||
</defs>
|
||||
<use xlink:href="#a" x="20" y="40">
|
||||
<set attributeName="xlink:href" to="#b" dur="2s"/>
|
||||
</use>
|
||||
</svg>
|
After Width: | Height: | Size: 276 B |
10
content/smil/crashtests/572938-4.svg
Normal file
10
content/smil/crashtests/572938-4.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="a">
|
||||
<path d=""><animate/></path>
|
||||
</g>
|
||||
<g display="none">
|
||||
<use xlink:href="#a" x="80"/>
|
||||
<set attributeName="display" to="inline"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 237 B |
25
content/smil/crashtests/605345-1.svg
Normal file
25
content/smil/crashtests/605345-1.svg
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
|
||||
<script>
|
||||
<![CDATA[
|
||||
|
||||
function boom()
|
||||
{
|
||||
var anim = document.getElementById("a");
|
||||
var newSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
var oldSvg = document.removeChild(document.documentElement);
|
||||
document.appendChild(newSvg);
|
||||
document.removeChild(document.documentElement);
|
||||
newSvg.pauseAnimations();
|
||||
document.appendChild(newSvg);
|
||||
newSvg.appendChild(anim);
|
||||
|
||||
oldSvg.removeAttribute("class");
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() { setTimeout(boom, 200); }, false);
|
||||
|
||||
]]>
|
||||
</script>
|
||||
<animate id="a"/>
|
||||
</svg>
|
After Width: | Height: | Size: 634 B |
29
content/smil/crashtests/608549-1.svg
Normal file
29
content/smil/crashtests/608549-1.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
|
||||
<script>
|
||||
<![CDATA[
|
||||
|
||||
function boom()
|
||||
{
|
||||
try {
|
||||
document.getElementById("set").beginElementAt(NaN);
|
||||
return;
|
||||
} catch (e) {}
|
||||
try {
|
||||
document.getElementById("set").endElementAt(NaN);
|
||||
return;
|
||||
} catch (e) {}
|
||||
|
||||
// If we got here we threw both exceptions and skipped both early-returns, as
|
||||
// expected.
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
window.addEventListener("load", boom, false);
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<set id="set" attributeName="fill" to="green" begin="indefinite"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 595 B |
@ -16,11 +16,17 @@ load 554202-2.svg
|
||||
load 554141-1.svg
|
||||
load 555026-1.svg
|
||||
load 556841-1.svg
|
||||
load 572938-1.svg
|
||||
load 572938-2.svg
|
||||
load 572938-3.svg
|
||||
load 572938-4.svg
|
||||
load 588287-1.svg
|
||||
load 588287-2.svg
|
||||
load 590425-1.html
|
||||
load 592477-1.xhtml
|
||||
load 594653-1.svg
|
||||
load 596796-1.svg
|
||||
load 605345-1.svg
|
||||
load 606101-1.svg
|
||||
load 608549-1.svg
|
||||
load 608295-1.html
|
||||
|
@ -74,9 +74,7 @@ GetRefreshDriverForDoc(nsIDocument* aDoc)
|
||||
nsSMILAnimationController::nsSMILAnimationController()
|
||||
: mResampleNeeded(PR_FALSE),
|
||||
mDeferredStartSampling(PR_FALSE),
|
||||
#ifdef DEBUG
|
||||
mRunningSample(PR_FALSE),
|
||||
#endif
|
||||
mDocument(nsnull)
|
||||
{
|
||||
mAnimationElementTable.Init();
|
||||
@ -189,7 +187,6 @@ void
|
||||
nsSMILAnimationController::RegisterAnimationElement(
|
||||
nsISMILAnimationElement* aAnimationElement)
|
||||
{
|
||||
NS_ASSERTION(!mRunningSample, "Registering content during sample.");
|
||||
mAnimationElementTable.PutEntry(aAnimationElement);
|
||||
if (mDeferredStartSampling) {
|
||||
mDeferredStartSampling = PR_FALSE;
|
||||
@ -207,7 +204,6 @@ void
|
||||
nsSMILAnimationController::UnregisterAnimationElement(
|
||||
nsISMILAnimationElement* aAnimationElement)
|
||||
{
|
||||
NS_ASSERTION(!mRunningSample, "Unregistering content during sample.");
|
||||
mAnimationElementTable.RemoveEntry(aAnimationElement);
|
||||
}
|
||||
|
||||
@ -362,16 +358,11 @@ nsSMILAnimationController::DoSample()
|
||||
void
|
||||
nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers)
|
||||
{
|
||||
// Reset resample flag -- do this before flushing styles since flushing styles
|
||||
// will also flush animation resample requests
|
||||
mResampleNeeded = PR_FALSE;
|
||||
mDocument->FlushPendingNotifications(Flush_Style);
|
||||
#ifdef DEBUG
|
||||
// Set running sample flag -- do this before flushing styles so that when we
|
||||
// flush styles we don't end up requesting extra samples
|
||||
mRunningSample = PR_TRUE;
|
||||
#endif
|
||||
// Reset resample flag again -- flushing styles may have set this flag but
|
||||
// since we're about to do a sample now, reset it
|
||||
mResampleNeeded = PR_FALSE;
|
||||
mDocument->FlushPendingNotifications(Flush_Style);
|
||||
|
||||
// STEP 1: Bring model up to date
|
||||
// (i) Rewind elements where necessary
|
||||
@ -445,9 +436,7 @@ nsSMILAnimationController::DoSample(PRBool aSkipUnchangedContainers)
|
||||
// when the inherited value is *also* being animated, we really should be
|
||||
// traversing our animated nodes in an ancestors-first order (bug 501183)
|
||||
currentCompositorTable->EnumerateEntries(DoComposeAttribute, nsnull);
|
||||
#ifdef DEBUG
|
||||
mRunningSample = PR_FALSE;
|
||||
#endif
|
||||
|
||||
// Update last compositor table
|
||||
mLastCompositorTable = currentCompositorTable.forget();
|
||||
|
@ -94,7 +94,12 @@ public:
|
||||
// (A resample performs the same operations as a sample but doesn't advance
|
||||
// the current time and doesn't check if the container is paused)
|
||||
void Resample() { DoSample(PR_FALSE); }
|
||||
void SetResampleNeeded() { mResampleNeeded = PR_TRUE; }
|
||||
void SetResampleNeeded()
|
||||
{
|
||||
if (!mRunningSample) {
|
||||
mResampleNeeded = PR_TRUE;
|
||||
}
|
||||
}
|
||||
void FlushResampleRequests()
|
||||
{
|
||||
if (!mResampleNeeded)
|
||||
@ -203,9 +208,7 @@ protected:
|
||||
// record the time, set the following flag, and then wait until we have an
|
||||
// animation element. Then we'll reset this flag and actually start sampling.
|
||||
PRPackedBool mDeferredStartSampling;
|
||||
#ifdef DEBUG
|
||||
PRPackedBool mRunningSample;
|
||||
#endif
|
||||
|
||||
// Store raw ptr to mDocument. It owns the controller, so controller
|
||||
// shouldn't outlive it
|
||||
|
@ -246,7 +246,7 @@ nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
|
||||
mSimpleDuration.IsIndefinite() || mLastValue,
|
||||
"Unresolved simple duration for active or frozen animation");
|
||||
|
||||
nsSMILValue result(aResult.mType);
|
||||
nsSMILValue result;
|
||||
|
||||
if (mSimpleDuration.IsIndefinite() ||
|
||||
(values.Length() == 1 && TreatSingleValueAsStatic())) {
|
||||
@ -402,15 +402,19 @@ nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues,
|
||||
// NS_ABORT_IF_FALSE that tests that intervalProgress is in range will fail.
|
||||
double intervalProgress = -1.f;
|
||||
if (IsToAnimation()) {
|
||||
from = &aBaseValue;
|
||||
to = &aValues[0];
|
||||
if (calcMode == CALC_PACED) {
|
||||
// Note: key[Times/Splines/Points] are ignored for calcMode="paced"
|
||||
intervalProgress = simpleProgress;
|
||||
if (aBaseValue.IsNull()) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
} else {
|
||||
double scaledSimpleProgress =
|
||||
ScaleSimpleProgress(simpleProgress, calcMode);
|
||||
intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0);
|
||||
from = &aBaseValue;
|
||||
to = &aValues[0];
|
||||
if (calcMode == CALC_PACED) {
|
||||
// Note: key[Times/Splines/Points] are ignored for calcMode="paced"
|
||||
intervalProgress = simpleProgress;
|
||||
} else {
|
||||
double scaledSimpleProgress =
|
||||
ScaleSimpleProgress(simpleProgress, calcMode);
|
||||
intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (calcMode == CALC_PACED) {
|
||||
|
@ -201,7 +201,7 @@ public:
|
||||
* @return True if the animation will replace, false if it will add or
|
||||
* otherwise build on the passed in value.
|
||||
*/
|
||||
PRBool WillReplace() const;
|
||||
virtual PRBool WillReplace() const;
|
||||
|
||||
/**
|
||||
* Indicates if the parameters for this animation have changed since the last
|
||||
|
@ -102,18 +102,21 @@ nsSMILCSSProperty::GetBaseValue() const
|
||||
// from ALL return points. This function must only return THIS variable:
|
||||
nsSMILValue baseValue;
|
||||
|
||||
// SPECIAL CASE: Shorthands
|
||||
if (nsCSSProps::IsShorthand(mPropID)) {
|
||||
// SPECIAL CASE: (a) Shorthands
|
||||
// (b) 'display'
|
||||
if (nsCSSProps::IsShorthand(mPropID) || mPropID == eCSSProperty_display) {
|
||||
// We can't look up the base (computed-style) value of shorthand
|
||||
// properties, because they aren't guaranteed to have a consistent computed
|
||||
// value. However, that's not a problem, because it turns out the caller
|
||||
// isn't going to end up using the value we return anyway. Base values only
|
||||
// get used when there's interpolation or addition, and the shorthand
|
||||
// properties we know about don't support those operations. So, we can just
|
||||
// return a dummy value (initialized with the right type, so as not to
|
||||
// indicate failure).
|
||||
nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton);
|
||||
baseValue.Swap(tmpVal);
|
||||
// properties because they aren't guaranteed to have a consistent computed
|
||||
// value.
|
||||
//
|
||||
// Also, although we can look up the base value of the display property,
|
||||
// doing so involves clearing and resetting the property which can cause
|
||||
// frames to be recreated which we'd like to avoid.
|
||||
//
|
||||
// In either case, simply returning a null-typed nsSMILValue to indicate
|
||||
// failure is acceptable because the caller only uses base values when
|
||||
// there's interpolation or addition, and for both the shorthand properties
|
||||
// we know about and the display property those operations aren't supported.
|
||||
return baseValue;
|
||||
}
|
||||
|
||||
|
@ -108,10 +108,9 @@ nsSMILCompositor::ComposeAttribute()
|
||||
PRUint32 firstFuncToCompose = GetFirstFuncToAffectSandwich();
|
||||
|
||||
// FOURTH: Get & cache base value
|
||||
nsSMILValue sandwichResultValue = smilAttr->GetBaseValue();
|
||||
if (sandwichResultValue.IsNull()) {
|
||||
NS_WARNING("nsISMILAttr::GetBaseValue failed");
|
||||
return;
|
||||
nsSMILValue sandwichResultValue;
|
||||
if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
|
||||
sandwichResultValue = smilAttr->GetBaseValue();
|
||||
}
|
||||
UpdateCachedBaseValue(sandwichResultValue);
|
||||
|
||||
@ -124,6 +123,10 @@ nsSMILCompositor::ComposeAttribute()
|
||||
for (PRUint32 i = firstFuncToCompose; i < length; ++i) {
|
||||
mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
|
||||
}
|
||||
if (sandwichResultValue.IsNull()) {
|
||||
smilAttr->ClearAnimValue();
|
||||
return;
|
||||
}
|
||||
|
||||
// SIXTH: Set the animated value to the final composited result.
|
||||
nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
|
||||
|
@ -124,3 +124,9 @@ nsSMILSetAnimationFunction::GetAttr(nsIAtom* aAttName,
|
||||
|
||||
return nsSMILAnimationFunction::GetAttr(aAttName, aResult);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSMILSetAnimationFunction::WillReplace() const
|
||||
{
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ protected:
|
||||
NS_OVERRIDE virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
|
||||
NS_OVERRIDE virtual PRBool GetAttr(nsIAtom* aAttName,
|
||||
nsAString& aResult) const;
|
||||
NS_OVERRIDE virtual PRBool WillReplace() const;
|
||||
|
||||
PRBool IsDisallowedAttribute(const nsIAtom* aAttribute) const;
|
||||
};
|
||||
|
@ -215,6 +215,14 @@ nsSMILTimeContainer::SetParent(nsSMILTimeContainer* aParent)
|
||||
{
|
||||
if (mParent) {
|
||||
mParent->RemoveChild(*this);
|
||||
// When we're not attached to a parent time container, GetParentTime() will
|
||||
// return 0. We need to adjust our pause state information to be relative to
|
||||
// this new time base.
|
||||
// Note that since "current time = parent time - parent offset" setting the
|
||||
// parent offset and pause start as follows preserves our current time even
|
||||
// while parent time = 0.
|
||||
mParentOffset = -mCurrentTime;
|
||||
mPauseStart = 0L;
|
||||
}
|
||||
|
||||
mParent = aParent;
|
||||
@ -314,6 +322,7 @@ nsSMILTimeContainer::UpdateCurrentTime()
|
||||
{
|
||||
nsSMILTime now = IsPaused() ? mPauseStart : GetParentTime();
|
||||
mCurrentTime = now - mParentOffset;
|
||||
NS_ABORT_IF_FALSE(mCurrentTime >= 0, "Container has negative time");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1104,6 +1104,10 @@ nsSMILTimedElement::IsTimeDependent(const nsSMILTimedElement& aOther) const
|
||||
void
|
||||
nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
|
||||
{
|
||||
// Reset previously registered milestone since we may be registering with
|
||||
// a different time container now.
|
||||
mPrevRegisteredMilestone = sMaxMilestone;
|
||||
|
||||
// If we were already active then clear all our timing information and start
|
||||
// afresh
|
||||
if (mElementState != STATE_STARTUP) {
|
||||
@ -1122,8 +1126,6 @@ nsSMILTimedElement::BindToTree(nsIContent* aContextNode)
|
||||
mEndSpecs[j]->ResolveReferences(aContextNode);
|
||||
}
|
||||
|
||||
// Register new milestone
|
||||
mPrevRegisteredMilestone = sMaxMilestone;
|
||||
RegisterMilestone();
|
||||
}
|
||||
|
||||
|
@ -113,12 +113,18 @@ const gValidPath = [
|
||||
"m0 0", "M0, 0"
|
||||
];
|
||||
|
||||
// paths must start with at least a valid "M" segment to be valid
|
||||
const gInvalidPath = [
|
||||
"m0 0 L30,,30",
|
||||
"M20 20em",
|
||||
"M20in 20",
|
||||
"h30",
|
||||
"L50 50",
|
||||
"abc",
|
||||
];
|
||||
|
||||
// paths that at least start with a valid "M" segment are valid - the spec says
|
||||
// to parse everything up to the first invalid token
|
||||
const gValidPathWithErrors = [
|
||||
"M20 20em",
|
||||
"m0 0 L30,,30",
|
||||
"M10 10 L50 50 abc",
|
||||
];
|
||||
|
@ -157,6 +157,7 @@ function main()
|
||||
|
||||
testAttr("path", gValidPath, true, false);
|
||||
testAttr("path", gInvalidPath, false, false);
|
||||
testAttr("path", gValidPathWithErrors, true, false);
|
||||
|
||||
testMpathElem(gValidPath, true, false);
|
||||
testMpathElem(gInvalidPath, false, false);
|
||||
|
1056
content/svg/content/src/DOMSVGPathSeg.cpp
Normal file
1056
content/svg/content/src/DOMSVGPathSeg.cpp
Normal file
File diff suppressed because it is too large
Load Diff
276
content/svg/content/src/DOMSVGPathSeg.h
Normal file
276
content/svg/content/src/DOMSVGPathSeg.h
Normal file
@ -0,0 +1,276 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_DOMSVGPATHSEG_H__
|
||||
#define MOZILLA_DOMSVGPATHSEG_H__
|
||||
|
||||
#include "nsIDOMSVGPathSeg.h"
|
||||
#include "DOMSVGPathSegList.h"
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsSVGElement;
|
||||
|
||||
// We make DOMSVGPathSeg a pseudo-interface to allow us to QI to it in order to
|
||||
// check that the objects that scripts pass to DOMSVGPathSegList methods are
|
||||
// our *native* path seg objects.
|
||||
//
|
||||
// {494A7566-DC26-40C8-9122-52ABD76870C4}
|
||||
#define MOZILLA_DOMSVGPATHSEG_IID \
|
||||
{ 0x494A7566, 0xDC26, 0x40C8, { 0x91, 0x22, 0x52, 0xAB, 0xD7, 0x68, 0x70, 0xC4 } }
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Class DOMSVGPathSeg
|
||||
*
|
||||
* This class is the base class of the classes that create the DOM objects that
|
||||
* wrap the internal path segments that are encoded in an SVGPathData. Its
|
||||
* sub-classes are also used to create the objects returned by
|
||||
* SVGPathElement.createSVGPathSegXxx().
|
||||
*
|
||||
* See the architecture comment in DOMSVGPathSegList.h for an overview of the
|
||||
* important points regarding these DOM wrapper structures.
|
||||
*
|
||||
* See the architecture comment in DOMSVGLength.h (yes, LENGTH) for an overview
|
||||
* of the important points regarding how this specific class works.
|
||||
*
|
||||
* The main differences between this class and DOMSVGLength is that we have
|
||||
* sub-classes (it does not), and the "internal counterpart" that we provide a
|
||||
* DOM wrapper for is a list of floats, not an instance of an internal class.
|
||||
*/
|
||||
class DOMSVGPathSeg : public nsIDOMSVGPathSeg
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGPATHSEG_IID)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGPathSeg)
|
||||
NS_DECL_NSIDOMSVGPATHSEG
|
||||
|
||||
/**
|
||||
* This convenient factory method creates instances of the correct sub-class.
|
||||
*/
|
||||
static DOMSVGPathSeg *CreateFor(DOMSVGPathSegList *aList,
|
||||
PRUint32 aListIndex,
|
||||
PRBool aIsAnimValItem);
|
||||
|
||||
/**
|
||||
* Create an unowned copy of this object. The caller is responsible for the
|
||||
* first AddRef()!
|
||||
*/
|
||||
virtual DOMSVGPathSeg* Clone() = 0;
|
||||
|
||||
PRBool IsInList() const {
|
||||
return !!mList;
|
||||
}
|
||||
|
||||
/**
|
||||
* In future, if this class is used for non-list segments, this will be
|
||||
* different to IsInList().
|
||||
*/
|
||||
PRBool HasOwner() const {
|
||||
return !!mList;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to notify this DOM object that it is being inserted
|
||||
* into a list, and give it the information it needs as a result.
|
||||
*
|
||||
* This object MUST NOT already belong to a list when this method is called.
|
||||
* That's not to say that script can't move these DOM objects between
|
||||
* lists - it can - it's just that the logic to handle that (and send out
|
||||
* the necessary notifications) is located elsewhere (in DOMSVGPathSegList).)
|
||||
*/
|
||||
void InsertingIntoList(DOMSVGPathSegList *aList,
|
||||
PRUint32 aListIndex,
|
||||
PRBool aIsAnimValItem);
|
||||
|
||||
/// This method is called to notify this object that its list index changed.
|
||||
void UpdateListIndex(PRUint8 aListIndex) {
|
||||
mListIndex = aListIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to notify this DOM object that it is about to be
|
||||
* removed from its current DOM list so that it can first make a copy of its
|
||||
* internal counterpart's values. (If it didn't do this, then it would
|
||||
* "lose" its value on being removed.)
|
||||
*/
|
||||
void RemovingFromList();
|
||||
|
||||
/**
|
||||
* This method converts the segment to a string of floats as found in
|
||||
* SVGPathData (i.e. the first float contains the type of the segment,
|
||||
* encoded into a float, followed by its arguments in the same order as they
|
||||
* are given in the <path> element's 'd' attribute).
|
||||
*/
|
||||
void ToSVGPathSegEncodedData(float *aData);
|
||||
|
||||
/**
|
||||
* The type of this path segment.
|
||||
*/
|
||||
virtual PRUint32 Type() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Generic ctor for DOMSVGPathSeg objects that are created for an attribute.
|
||||
*/
|
||||
DOMSVGPathSeg(DOMSVGPathSegList *aList,
|
||||
PRUint32 aListIndex,
|
||||
PRBool aIsAnimValItem);
|
||||
|
||||
/**
|
||||
* Ctor for creating the objects returned by
|
||||
* SVGPathElement.createSVGPathSegXxx(), which do not initially belong to an
|
||||
* attribute.
|
||||
*/
|
||||
DOMSVGPathSeg();
|
||||
|
||||
virtual ~DOMSVGPathSeg() {
|
||||
// Our mList's weak ref to us must be nulled out when we die. If GC has
|
||||
// unlinked us using the cycle collector code, then that has already
|
||||
// happened, and mList is null.
|
||||
if (mList) {
|
||||
mList->ItemAt(mListIndex) = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
nsSVGElement* Element() {
|
||||
return mList->Element();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to the internal SVGPathSeg list item that this DOM wrapper
|
||||
* object currently wraps.
|
||||
*
|
||||
* To simplify the code we just have this one method for obtaining both
|
||||
* baseVal and animVal internal items. This means that animVal items don't
|
||||
* get const protection, but then our setter methods guard against changing
|
||||
* animVal items.
|
||||
*/
|
||||
float* InternalItem();
|
||||
|
||||
virtual float* PtrToMemberArgs() = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
PRBool IndexIsValid();
|
||||
#endif
|
||||
|
||||
nsRefPtr<DOMSVGPathSegList> mList;
|
||||
|
||||
// Bounds for the following are checked in the ctor, so be sure to update
|
||||
// that if you change the capacity of any of the following.
|
||||
|
||||
PRUint32 mListIndex:31;
|
||||
PRUint32 mIsAnimValItem:1; // PRUint32 because MSVC won't pack otherwise
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGPathSeg, MOZILLA_DOMSVGPATHSEG_IID)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegClosePath();
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegMovetoAbs(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegMovetoRel(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoAbs(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoRel(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicAbs(float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicRel(float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticAbs(float x, float y,
|
||||
float x1, float y1);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticRel(float x, float y,
|
||||
float x1, float y1);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegArcAbs(float x, float y,
|
||||
float r1, float r2, float angle,
|
||||
PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegArcRel(float x, float y,
|
||||
float r1, float r2, float angle,
|
||||
PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoHorizontalAbs(float x);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoHorizontalRel(float x);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoVerticalAbs(float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoVerticalRel(float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicSmoothAbs(float x, float y,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicSmoothRel(float x, float y,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticSmoothRel(float x, float y);
|
||||
|
||||
#endif // MOZILLA_DOMSVGPATHSEG_H__
|
489
content/svg/content/src/DOMSVGPathSegList.cpp
Normal file
489
content/svg/content/src/DOMSVGPathSegList.cpp
Normal file
@ -0,0 +1,489 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsSVGElement.h"
|
||||
#include "DOMSVGPathSegList.h"
|
||||
#include "DOMSVGPathSeg.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "SVGAnimatedPathSegList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
|
||||
// See the comment in this file's header.
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static nsSVGAttrTearoffTable<void, DOMSVGPathSegList>
|
||||
sSVGPathSegListTearoffTable;
|
||||
|
||||
NS_SVG_VAL_IMPL_CYCLE_COLLECTION(DOMSVGPathSegList, mElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGPathSegList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGPathSegList)
|
||||
|
||||
DOMCI_DATA(SVGPathSegList, DOMSVGPathSegList)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGPathSegList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPathSegList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPathSegList)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
/* static */ already_AddRefed<DOMSVGPathSegList>
|
||||
DOMSVGPathSegList::GetDOMWrapper(void *aList,
|
||||
nsSVGElement *aElement,
|
||||
PRBool aIsAnimValList)
|
||||
{
|
||||
DOMSVGPathSegList *wrapper =
|
||||
sSVGPathSegListTearoffTable.GetTearoff(aList);
|
||||
if (!wrapper) {
|
||||
wrapper = new DOMSVGPathSegList(aElement, aIsAnimValList);
|
||||
sSVGPathSegListTearoffTable.AddTearoff(aList, wrapper);
|
||||
}
|
||||
NS_ADDREF(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/* static */ DOMSVGPathSegList*
|
||||
DOMSVGPathSegList::GetDOMWrapperIfExists(void *aList)
|
||||
{
|
||||
return sSVGPathSegListTearoffTable.GetTearoff(aList);
|
||||
}
|
||||
|
||||
DOMSVGPathSegList::~DOMSVGPathSegList()
|
||||
{
|
||||
// We no longer have any list items, and there are no script references to
|
||||
// us.
|
||||
//
|
||||
// Do NOT use InternalList() here! That's different!
|
||||
void *key = mIsAnimValList ?
|
||||
InternalAList().GetAnimValKey() :
|
||||
InternalAList().GetBaseValKey();
|
||||
sSVGPathSegListTearoffTable.RemoveTearoff(key);
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue)
|
||||
{
|
||||
// When the number of items in our internal counterpart changes, we MUST stay
|
||||
// in sync. Everything in the scary comment in
|
||||
// DOMSVGLengthList::InternalBaseValListWillChangeTo applies here just as
|
||||
// much, but we have the additional issue that failing to stay in sync would
|
||||
// mean that - assuming we aren't reading bad memory - we would likely end up
|
||||
// decoding command types from argument floats when looking in our
|
||||
// SVGPathData's data array! Either way, we'll likely then go down
|
||||
// NS_NOTREACHED code paths, or end up reading/setting more bad memory!!
|
||||
|
||||
// The only time that our other DOM list type implementations remove items is
|
||||
// if those items become surplus items due to an attribute change or SMIL
|
||||
// animation sample shortening the list. In general though, they try to keep
|
||||
// their existing DOM items, even when things change. To be consistent, we'd
|
||||
// really like to do the same thing. However, because different types of path
|
||||
// segment correspond to different DOMSVGPathSeg subclasses, the type of
|
||||
// items in our list are generally not the same, which makes this harder for
|
||||
// us. We have to remove DOM segments if their type is not the same as the
|
||||
// type of the new internal segment at their index.
|
||||
//
|
||||
// We also need to sync up mInternalDataIndex, but since we need to loop over
|
||||
// all the items in the new list checking types anyway, that's almost
|
||||
// insignificant in terms of overhead.
|
||||
//
|
||||
// Note that this method is called on every single SMIL animation resample
|
||||
// and we have no way to short circuit the overhead since we don't have a
|
||||
// way to tell if the call is due to a new animation, or a resample of an
|
||||
// existing animation (when the number and type of items would be the same).
|
||||
// (Note that a new animation could start overriding an existing animation at
|
||||
// any time, so checking IsAnimating() wouldn't work.) Because we get called
|
||||
// on every sample, it would not be acceptable alternative to throw away all
|
||||
// our items and let them be recreated lazily, since that would break what
|
||||
// script sees!
|
||||
|
||||
PRUint32 length = mItems.Length();
|
||||
PRUint32 index = 0;
|
||||
|
||||
PRUint32 dataLength = aNewValue.mData.Length();
|
||||
PRUint32 dataIndex = 0; // index into aNewValue's raw data array
|
||||
|
||||
PRUint32 newSegType;
|
||||
|
||||
while (index < length && dataIndex < dataLength) {
|
||||
newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]);
|
||||
if (ItemAt(index) && ItemAt(index)->Type() != newSegType) {
|
||||
ItemAt(index)->RemovingFromList();
|
||||
ItemAt(index) = nsnull;
|
||||
}
|
||||
// Only after the RemovingFromList() can we touch mInternalDataIndex!
|
||||
mItems[index].mInternalDataIndex = dataIndex;
|
||||
++index;
|
||||
dataIndex += 1 + SVGPathSegUtils::ArgCountForType(newSegType);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE((index == length && dataIndex <= dataLength) ||
|
||||
(index <= length && dataIndex == dataLength),
|
||||
"very bad - list corruption?");
|
||||
|
||||
if (index < length) {
|
||||
// aNewValue has fewer items than our previous internal counterpart
|
||||
|
||||
PRUint32 newLength = index;
|
||||
|
||||
// Remove excess items from the list:
|
||||
for (; index < length; ++index) {
|
||||
if (ItemAt(index)) {
|
||||
ItemAt(index)->RemovingFromList();
|
||||
ItemAt(index) = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
// Only now may we truncate mItems
|
||||
mItems.SetLength(newLength);
|
||||
|
||||
} else if (dataIndex < dataLength) {
|
||||
// aNewValue has more items than our previous internal counterpart
|
||||
|
||||
// Sync mItems:
|
||||
while (dataIndex < dataLength) {
|
||||
if (!mItems.AppendElement(ItemProxy(nsnull, dataIndex))) {
|
||||
// OOM
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
dataIndex += 1 + SVGPathSegUtils::ArgCountForType(SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(dataIndex == dataLength, "Serious processing error");
|
||||
NS_ABORT_IF_FALSE(index == length, "Serious counting error");
|
||||
}
|
||||
|
||||
PRBool
|
||||
DOMSVGPathSegList::AttrIsAnimating() const
|
||||
{
|
||||
return const_cast<DOMSVGPathSegList*>(this)->InternalAList().IsAnimating();
|
||||
}
|
||||
|
||||
SVGPathData&
|
||||
DOMSVGPathSegList::InternalList()
|
||||
{
|
||||
SVGAnimatedPathSegList *alist = mElement->GetAnimPathSegList();
|
||||
return mIsAnimValList && alist->IsAnimating() ? *alist->mAnimVal : alist->mBaseVal;
|
||||
}
|
||||
|
||||
SVGAnimatedPathSegList&
|
||||
DOMSVGPathSegList::InternalAList()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mElement->GetAnimPathSegList(), "Internal error");
|
||||
return *mElement->GetAnimPathSegList();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// nsIDOMSVGPathSegList implementation:
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::GetNumberOfItems(PRUint32 *aNumberOfItems)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
}
|
||||
#endif
|
||||
*aNumberOfItems = Length();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::Clear()
|
||||
{
|
||||
if (IsAnimValList()) {
|
||||
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
if (Length() > 0) {
|
||||
// DOM list items that are to be removed must be removed before we change
|
||||
// the internal list, otherwise they wouldn't be able to copy their
|
||||
// internal counterparts' values!
|
||||
|
||||
InternalListWillChangeTo(SVGPathData()); // clears mItems
|
||||
|
||||
if (!AttrIsAnimating()) {
|
||||
// The anim val list is in sync with the base val list
|
||||
DOMSVGPathSegList *animList =
|
||||
GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
|
||||
if (animList) {
|
||||
animList->InternalListWillChangeTo(SVGPathData()); // clears its mItems
|
||||
}
|
||||
}
|
||||
|
||||
InternalList().Clear();
|
||||
Element()->DidChangePathSegList(PR_TRUE);
|
||||
#ifdef MOZ_SMIL
|
||||
if (AttrIsAnimating()) {
|
||||
Element()->AnimationNeedsResample();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::Initialize(nsIDOMSVGPathSeg *aNewItem,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
if (IsAnimValList()) {
|
||||
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
// If aNewItem is already in a list we should insert a clone of aNewItem,
|
||||
// and for consistency, this should happen even if *this* is the list that
|
||||
// aNewItem is currently in. Note that in the case of aNewItem being in this
|
||||
// list, the Clear() call before the InsertItemBefore() call would remove it
|
||||
// from this list, and so the InsertItemBefore() call would not insert a
|
||||
// clone of aNewItem, it would actually insert aNewItem. To prevent that
|
||||
// from happening we have to do the clone here, if necessary.
|
||||
|
||||
nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem);
|
||||
if (!domItem) {
|
||||
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
|
||||
}
|
||||
if (domItem->HasOwner()) {
|
||||
aNewItem = domItem->Clone();
|
||||
}
|
||||
|
||||
Clear();
|
||||
return InsertItemBefore(aNewItem, 0, _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::GetItem(PRUint32 aIndex,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
NS_ADDREF(*_retval = ItemAt(aIndex));
|
||||
return NS_OK;
|
||||
}
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *aNewItem,
|
||||
PRUint32 aIndex,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
if (IsAnimValList()) {
|
||||
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem);
|
||||
if (!domItem) {
|
||||
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
|
||||
}
|
||||
if (domItem->HasOwner()) {
|
||||
domItem = domItem->Clone(); // must do this before changing anything!
|
||||
}
|
||||
PRUint32 internalIndex;
|
||||
if (aIndex < Length()) {
|
||||
internalIndex = mItems[aIndex].mInternalDataIndex;
|
||||
} else {
|
||||
aIndex = Length();
|
||||
internalIndex = InternalList().mData.Length();
|
||||
}
|
||||
PRUint32 argCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
|
||||
|
||||
// Ensure we have enough memory so we can avoid complex error handling below:
|
||||
if (!mItems.SetCapacity(mItems.Length() + 1) ||
|
||||
!InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
|
||||
domItem->ToSVGPathSegEncodedData(segAsRaw);
|
||||
|
||||
InternalList().mData.InsertElementsAt(internalIndex, segAsRaw, 1 + argCount);
|
||||
mItems.InsertElementAt(aIndex, ItemProxy(domItem.get(), internalIndex));
|
||||
|
||||
// This MUST come after the insertion into InternalList(), or else under the
|
||||
// insertion into InternalList() the data read from domItem would be bad data
|
||||
// from InternalList() itself!:
|
||||
domItem->InsertingIntoList(this, aIndex, IsAnimValList());
|
||||
|
||||
for (PRUint32 i = aIndex + 1; i < Length(); ++i) {
|
||||
mItems[i].mInternalDataIndex += 1 + argCount;
|
||||
if (ItemAt(i)) {
|
||||
ItemAt(i)->UpdateListIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
Element()->DidChangePathSegList(PR_TRUE);
|
||||
#ifdef MOZ_SMIL
|
||||
if (AttrIsAnimating()) {
|
||||
Element()->AnimationNeedsResample();
|
||||
}
|
||||
#endif
|
||||
*_retval = domItem.forget().get();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *aNewItem,
|
||||
PRUint32 aIndex,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
if (IsAnimValList()) {
|
||||
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<DOMSVGPathSeg> domItem = do_QueryInterface(aNewItem);
|
||||
if (!domItem) {
|
||||
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
|
||||
}
|
||||
if (aIndex >= Length()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
if (domItem->HasOwner()) {
|
||||
domItem = domItem->Clone(); // must do this before changing anything!
|
||||
}
|
||||
|
||||
if (ItemAt(aIndex)) {
|
||||
// Notify any existing DOM item of removal *before* modifying the lists so
|
||||
// that the DOM item can copy the *old* value at its index:
|
||||
ItemAt(aIndex)->RemovingFromList();
|
||||
}
|
||||
|
||||
PRUint32 internalIndex = mItems[aIndex].mInternalDataIndex;
|
||||
PRUint32 oldArgCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
|
||||
PRUint32 newArgCount = SVGPathSegUtils::ArgCountForType(domItem->Type());
|
||||
|
||||
float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
|
||||
domItem->ToSVGPathSegEncodedData(segAsRaw);
|
||||
|
||||
PRBool ok = !!InternalList().mData.ReplaceElementsAt(
|
||||
internalIndex, 1 + oldArgCount,
|
||||
segAsRaw, 1 + newArgCount);
|
||||
if (!ok) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
ItemAt(aIndex) = domItem;
|
||||
|
||||
// This MUST come after the ToSVGPathSegEncodedData call otherwise that call
|
||||
// would end up reading bad data from InternalList()!
|
||||
domItem->InsertingIntoList(this, aIndex, IsAnimValList());
|
||||
|
||||
PRUint32 delta = newArgCount - oldArgCount;
|
||||
if (delta != 0) {
|
||||
for (PRUint32 i = aIndex + 1; i < Length(); ++i) {
|
||||
mItems[i].mInternalDataIndex += delta;
|
||||
}
|
||||
}
|
||||
|
||||
Element()->DidChangePathSegList(PR_TRUE);
|
||||
#ifdef MOZ_SMIL
|
||||
if (AttrIsAnimating()) {
|
||||
Element()->AnimationNeedsResample();
|
||||
}
|
||||
#endif
|
||||
NS_ADDREF(*_retval = domItem.get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::RemoveItem(PRUint32 aIndex,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
*_retval = nsnull;
|
||||
if (IsAnimValList()) {
|
||||
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
if (aIndex >= Length()) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
// We have to return the removed item, so make sure it exists:
|
||||
EnsureItemAt(aIndex);
|
||||
|
||||
// Notify the DOM item of removal *before* modifying the lists so that the
|
||||
// DOM item can copy its *old* value:
|
||||
ItemAt(aIndex)->RemovingFromList();
|
||||
NS_ADDREF(*_retval = ItemAt(aIndex));
|
||||
|
||||
PRUint32 internalIndex = mItems[aIndex].mInternalDataIndex;
|
||||
PRUint32 segType = SVGPathSegUtils::DecodeType(InternalList().mData[internalIndex]);
|
||||
PRUint32 argCount = SVGPathSegUtils::ArgCountForType(segType);
|
||||
|
||||
InternalList().mData.RemoveElementsAt(internalIndex, 1 + argCount);
|
||||
mItems.RemoveElementAt(aIndex);
|
||||
|
||||
for (PRUint32 i = aIndex; i < Length(); ++i) {
|
||||
mItems[i].mInternalDataIndex -= 1 + argCount;
|
||||
if (ItemAt(i)) {
|
||||
ItemAt(i)->UpdateListIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
Element()->DidChangePathSegList(PR_TRUE);
|
||||
#ifdef MOZ_SMIL
|
||||
if (AttrIsAnimating()) {
|
||||
Element()->AnimationNeedsResample();
|
||||
}
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::AppendItem(nsIDOMSVGPathSeg *aNewItem,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
return InsertItemBefore(aNewItem, Length(), _retval);
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGPathSegList::EnsureItemAt(PRUint32 aIndex)
|
||||
{
|
||||
if (!ItemAt(aIndex)) {
|
||||
ItemAt(aIndex) = DOMSVGPathSeg::CreateFor(this, aIndex, IsAnimValList());
|
||||
}
|
||||
}
|
||||
|
235
content/svg/content/src/DOMSVGPathSegList.h
Normal file
235
content/svg/content/src/DOMSVGPathSegList.h
Normal file
@ -0,0 +1,235 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_DOMSVGPATHSEGLIST_H__
|
||||
#define MOZILLA_DOMSVGPATHSEGLIST_H__
|
||||
|
||||
#include "nsIDOMSVGPathSegList.h"
|
||||
#include "SVGPathData.h"
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
class nsSVGElement;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DOMSVGPathSeg;
|
||||
class SVGAnimatedPathSegList;
|
||||
|
||||
/**
|
||||
* Class DOMSVGPathSegList
|
||||
*
|
||||
* This class is used to create the DOM tearoff objects that wrap internal
|
||||
* SVGPathData objects.
|
||||
*
|
||||
* See the architecture comment in DOMSVGAnimatedLengthList.h first (that's
|
||||
* LENGTH list), then continue reading the remainder of this comment.
|
||||
*
|
||||
* The architecture of this class is very similar to that of DOMSVGLengthList
|
||||
* except that, since there is no nsIDOMSVGAnimatedPathSegList interface
|
||||
* in SVG, we have no parent DOMSVGAnimatedPathSegList (unlike DOMSVGLengthList
|
||||
* which has a parent DOMSVGAnimatedLengthList class). (There is an
|
||||
* SVGAnimatedPathData interface, but that is quite different to
|
||||
* DOMSVGAnimatedLengthList, since it is inherited by elements rather than
|
||||
* elements having members of that type.) As a consequence, much of the logic
|
||||
* that would otherwise be in DOMSVGAnimatedPathSegList (and is in
|
||||
* DOMSVGAnimatedLengthList) is contained in this class.
|
||||
*
|
||||
* This class is strongly intertwined with DOMSVGPathSeg. Our DOMSVGPathSeg
|
||||
* items are friends of us and responsible for nulling out our pointers to
|
||||
* them when they die.
|
||||
*
|
||||
* Our DOM items are created lazily on demand as and when script requests them.
|
||||
*/
|
||||
class DOMSVGPathSegList : public nsIDOMSVGPathSegList
|
||||
{
|
||||
friend class DOMSVGPathSeg;
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGPathSegList)
|
||||
NS_DECL_NSIDOMSVGPATHSEGLIST
|
||||
|
||||
/**
|
||||
* Factory method to create and return a DOMSVGPathSegList wrapper
|
||||
* for a given internal SVGPathData object. The factory takes care
|
||||
* of caching the object that it returns so that the same object can be
|
||||
* returned for the given SVGPathData each time it is requested.
|
||||
* The cached object is only removed from the cache when it is destroyed due
|
||||
* to there being no more references to it or to any of its descendant
|
||||
* objects. If that happens, any subsequent call requesting the DOM wrapper
|
||||
* for the SVGPathData will naturally result in a new
|
||||
* DOMSVGPathSegList being returned.
|
||||
*
|
||||
* It's unfortunate that aList is a void* instead of a typed argument. This
|
||||
* is because the mBaseVal and mAnimVal members of SVGAnimatedPathSegList are
|
||||
* of different types - a plain SVGPathData, and a SVGPathData*. We
|
||||
* use the addresses of these members as the key for the hash table, and
|
||||
* clearly SVGPathData* and a SVGPathData** are not the same type.
|
||||
*/
|
||||
static already_AddRefed<DOMSVGPathSegList>
|
||||
GetDOMWrapper(void *aList,
|
||||
nsSVGElement *aElement,
|
||||
PRBool aIsAnimValList);
|
||||
|
||||
/**
|
||||
* This method returns the DOMSVGPathSegList wrapper for an internal
|
||||
* SVGPathData object if it currently has a wrapper. If it does
|
||||
* not, then nsnull is returned.
|
||||
*/
|
||||
static DOMSVGPathSegList*
|
||||
GetDOMWrapperIfExists(void *aList);
|
||||
|
||||
/**
|
||||
* This will normally be the same as InternalList().CountItems(), except if
|
||||
* we've hit OOM, in which case our length will be zero.
|
||||
*/
|
||||
PRUint32 Length() const {
|
||||
NS_ABORT_IF_FALSE(mItems.Length() == 0 ||
|
||||
mItems.Length() ==
|
||||
const_cast<DOMSVGPathSegList*>(this)->InternalList().CountItems(),
|
||||
"DOM wrapper's list length is out of sync");
|
||||
return mItems.Length();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* callers!
|
||||
*
|
||||
* Called by internal code to notify us when we need to sync the length of
|
||||
* this DOM list with its internal list. This is called immediately prior to
|
||||
* the length of the internal list being changed so that any DOM list items
|
||||
* that need to be removed from the DOM list can first copy their values from
|
||||
* their internal counterpart.
|
||||
*
|
||||
* The only time this method could fail is on OOM when trying to increase the
|
||||
* length of the DOM list. If that happens then this method simply clears the
|
||||
* list and returns. Callers just proceed as normal, and we simply accept
|
||||
* that the DOM list will be empty (until successfully set to a new value).
|
||||
*/
|
||||
void InternalListWillChangeTo(const SVGPathData& aNewValue);
|
||||
|
||||
/**
|
||||
* Returns true if our attribute is animating (in which case our animVal is
|
||||
* not simply a mirror of our baseVal).
|
||||
*/
|
||||
PRBool AttrIsAnimating() const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Only our static GetDOMWrapper() factory method may create objects of our
|
||||
* type.
|
||||
*/
|
||||
DOMSVGPathSegList(nsSVGElement *aElement, PRBool aIsAnimValList)
|
||||
: mElement(aElement)
|
||||
, mIsAnimValList(aIsAnimValList)
|
||||
{
|
||||
// This call populates mItems with the same number of items as there are
|
||||
// segments contained in the internal list. We ignore OOM failure since
|
||||
// being out of sync is safe so long as we have *fewer* items than our
|
||||
// internal list.
|
||||
|
||||
InternalListWillChangeTo(InternalList());
|
||||
}
|
||||
|
||||
~DOMSVGPathSegList();
|
||||
|
||||
nsSVGElement* Element() {
|
||||
return mElement.get();
|
||||
}
|
||||
|
||||
/// Used to determine if this list is the baseVal or animVal list.
|
||||
PRBool IsAnimValList() const {
|
||||
return mIsAnimValList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to this object's corresponding internal SVGPathData.
|
||||
*
|
||||
* To simplify the code we just have this one method for obtaining both
|
||||
* base val and anim val internal lists. This means that anim val lists don't
|
||||
* get const protection, but our setter methods guard against changing
|
||||
* anim val lists.
|
||||
*/
|
||||
SVGPathData& InternalList();
|
||||
|
||||
SVGAnimatedPathSegList& InternalAList();
|
||||
|
||||
/// Creates an instance of the appropriate DOMSVGPathSeg sub-class for
|
||||
// aIndex, if it doesn't already exist.
|
||||
void EnsureItemAt(PRUint32 aIndex);
|
||||
|
||||
DOMSVGPathSeg*& ItemAt(PRUint32 aIndex) {
|
||||
return mItems[aIndex].mItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* This struct is used in our array of mItems to provide us with somewhere to
|
||||
* store the indexes into the internal SVGPathData of the internal seg data
|
||||
* that our DOMSVGPathSeg items wrap (the internal segment data is or varying
|
||||
* length, so we can't just use the index of our DOMSVGPathSeg items
|
||||
* themselves). The reason that we have this separate struct rather than
|
||||
* just storing the internal indexes in the DOMSVGPathSeg items is because we
|
||||
* want to create the DOMSVGPathSeg items lazily on demand.
|
||||
*/
|
||||
struct ItemProxy {
|
||||
ItemProxy(){}
|
||||
ItemProxy(DOMSVGPathSeg *aItem, PRUint32 aInternalDataIndex)
|
||||
: mItem(aItem)
|
||||
, mInternalDataIndex(aInternalDataIndex)
|
||||
{}
|
||||
|
||||
DOMSVGPathSeg *mItem;
|
||||
PRUint32 mInternalDataIndex;
|
||||
};
|
||||
|
||||
// Weak refs to our DOMSVGPathSeg items. The items are friends and take care
|
||||
// of clearing our pointer to them when they die.
|
||||
nsTArray<ItemProxy> mItems;
|
||||
|
||||
// Strong ref to our element to keep it alive. We hold this not only for
|
||||
// ourself, but also for our DOMSVGPathSeg items too.
|
||||
nsRefPtr<nsSVGElement> mElement;
|
||||
|
||||
PRPackedBool mIsAnimValList;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_DOMSVGPATHSEGLIST_H__
|
@ -52,6 +52,8 @@ CPPSRCS = \
|
||||
DOMSVGAnimatedLengthList.cpp \
|
||||
DOMSVGLength.cpp \
|
||||
DOMSVGLengthList.cpp \
|
||||
DOMSVGPathSeg.cpp \
|
||||
DOMSVGPathSegList.cpp \
|
||||
nsDOMSVGZoomEvent.cpp \
|
||||
nsDOMSVGEvent.cpp \
|
||||
nsSVGAElement.cpp \
|
||||
@ -90,8 +92,6 @@ CPPSRCS = \
|
||||
nsSVGPathDataParser.cpp \
|
||||
nsSVGPathElement.cpp \
|
||||
nsSVGPathGeometryElement.cpp \
|
||||
nsSVGPathSeg.cpp \
|
||||
nsSVGPathSegList.cpp \
|
||||
nsSVGPatternElement.cpp \
|
||||
nsSVGPoint.cpp \
|
||||
nsSVGPointList.cpp \
|
||||
@ -123,8 +123,11 @@ CPPSRCS = \
|
||||
nsSVGValue.cpp \
|
||||
nsSVGViewBox.cpp \
|
||||
SVGAnimatedLengthList.cpp \
|
||||
SVGAnimatedPathSegList.cpp \
|
||||
SVGLength.cpp \
|
||||
SVGLengthList.cpp \
|
||||
SVGPathData.cpp \
|
||||
SVGPathSegUtils.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_SMIL
|
||||
@ -142,6 +145,7 @@ CPPSRCS += nsSVGAnimateElement.cpp \
|
||||
SVGMotionSMILAnimationFunction.cpp \
|
||||
SVGMotionSMILPathUtils.cpp \
|
||||
SVGOrientSMILType.cpp \
|
||||
SVGPathSegListSMILType.cpp \
|
||||
SVGViewBoxSMILType.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
240
content/svg/content/src/SVGAnimatedPathSegList.cpp
Normal file
240
content/svg/content/src/SVGAnimatedPathSegList.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "SVGAnimatedPathSegList.h"
|
||||
#include "DOMSVGPathSegList.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
#ifdef MOZ_SMIL
|
||||
#include "nsSMILValue.h"
|
||||
#include "SVGPathSegListSMILType.h"
|
||||
#endif // MOZ_SMIL
|
||||
|
||||
// See the comments in this file's header!
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsresult
|
||||
SVGAnimatedPathSegList::SetBaseValueString(const nsAString& aValue)
|
||||
{
|
||||
SVGPathData newBaseValue;
|
||||
|
||||
// The spec says that the path data is parsed and accepted up to the first
|
||||
// error encountered, so we don't return early if an error occurs. However,
|
||||
// we do want to throw any error code from setAttribute if there's a problem.
|
||||
|
||||
nsresult rv = newBaseValue.SetValueFromString(aValue);
|
||||
|
||||
// We must send these notifications *before* changing mBaseVal! Our baseVal's
|
||||
// DOM wrapper list may have to remove DOM items from itself, and any removed
|
||||
// DOM items need to copy their internal counterpart's values *before* we
|
||||
// change them. See the comments in
|
||||
// DOMSVGPathSegList::InternalListWillChangeTo().
|
||||
|
||||
DOMSVGPathSegList *baseValWrapper =
|
||||
DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
|
||||
if (baseValWrapper) {
|
||||
baseValWrapper->InternalListWillChangeTo(newBaseValue);
|
||||
}
|
||||
|
||||
DOMSVGPathSegList *animValWrapper;
|
||||
if (!IsAnimating()) { // DOM anim val wraps our base val too!
|
||||
animValWrapper = DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
|
||||
if (animValWrapper) {
|
||||
animValWrapper->InternalListWillChangeTo(newBaseValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Only now may we modify mBaseVal!
|
||||
|
||||
// We don't need to call DidChange* here - we're only called by
|
||||
// nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
|
||||
// which takes care of notifying.
|
||||
|
||||
nsresult rv2 = mBaseVal.CopyFrom(newBaseValue);
|
||||
if (NS_FAILED(rv2)) {
|
||||
// Attempting to increase mBaseVal's length failed (mBaseVal is left
|
||||
// unmodified). We MUST keep any DOM wrappers in sync:
|
||||
if (baseValWrapper) {
|
||||
baseValWrapper->InternalListWillChangeTo(mBaseVal);
|
||||
}
|
||||
if (animValWrapper) {
|
||||
animValWrapper->InternalListWillChangeTo(mBaseVal);
|
||||
}
|
||||
return rv2;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedPathSegList::ClearBaseValue()
|
||||
{
|
||||
// We must send these notifications *before* changing mBaseVal! (See above.)
|
||||
|
||||
DOMSVGPathSegList *baseValWrapper =
|
||||
DOMSVGPathSegList::GetDOMWrapperIfExists(GetBaseValKey());
|
||||
if (baseValWrapper) {
|
||||
baseValWrapper->InternalListWillChangeTo(SVGPathData());
|
||||
}
|
||||
|
||||
if (!IsAnimating()) { // DOM anim val wraps our base val too!
|
||||
DOMSVGPathSegList *animValWrapper =
|
||||
DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
|
||||
if (animValWrapper) {
|
||||
animValWrapper->InternalListWillChangeTo(SVGPathData());
|
||||
}
|
||||
}
|
||||
|
||||
mBaseVal.Clear();
|
||||
// Caller notifies
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGAnimatedPathSegList::SetAnimValue(const SVGPathData& aNewAnimValue,
|
||||
nsSVGElement *aElement)
|
||||
{
|
||||
// Note that a new animation may totally change the number of items in the
|
||||
// animVal list, either replacing what was essentially a mirror of the
|
||||
// baseVal list, or else replacing and overriding an existing animation.
|
||||
// Unfortunately it is not possible for us to reliably distinguish between
|
||||
// calls to this method that are setting a new sample for an existing
|
||||
// animation, and calls that are setting the first sample of an animation
|
||||
// that will override an existing animation. In the case of DOMSVGPathSegList
|
||||
// the InternalListWillChangeTo method is not virtually free as it is for the
|
||||
// other DOM list classes, so this is a shame. We'd quite like to be able to
|
||||
// skip the call if possible.
|
||||
|
||||
// We must send these notifications *before* changing mAnimVal! (See above.)
|
||||
|
||||
DOMSVGPathSegList *domWrapper =
|
||||
DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
|
||||
if (domWrapper) {
|
||||
domWrapper->InternalListWillChangeTo(aNewAnimValue);
|
||||
}
|
||||
if (!mAnimVal) {
|
||||
mAnimVal = new SVGPathData();
|
||||
}
|
||||
nsresult rv = mAnimVal->CopyFrom(aNewAnimValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
// OOM. We clear the animation and, importantly, ClearAnimValue() ensures
|
||||
// that mAnimVal's DOM wrapper (if any) is kept in sync!
|
||||
ClearAnimValue(aElement);
|
||||
}
|
||||
aElement->DidAnimatePathSegList();
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedPathSegList::ClearAnimValue(nsSVGElement *aElement)
|
||||
{
|
||||
// We must send these notifications *before* changing mAnimVal! (See above.)
|
||||
|
||||
DOMSVGPathSegList *domWrapper =
|
||||
DOMSVGPathSegList::GetDOMWrapperIfExists(GetAnimValKey());
|
||||
if (domWrapper) {
|
||||
// When all animation ends, animVal simply mirrors baseVal, which may have
|
||||
// a different number of items to the last active animated value.
|
||||
//
|
||||
domWrapper->InternalListWillChangeTo(mBaseVal);
|
||||
}
|
||||
mAnimVal = nsnull;
|
||||
aElement->DidAnimatePathSegList();
|
||||
}
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
nsISMILAttr*
|
||||
SVGAnimatedPathSegList::ToSMILAttr(nsSVGElement *aElement)
|
||||
{
|
||||
return new SMILAnimatedPathSegList(this, aElement);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGAnimatedPathSegList::
|
||||
SMILAnimatedPathSegList::ValueFromString(const nsAString& aStr,
|
||||
const nsISMILAnimationElement* /*aSrcElement*/,
|
||||
nsSMILValue& aValue,
|
||||
PRBool& aPreventCachingOfSandwich) const
|
||||
{
|
||||
nsSMILValue val(&SVGPathSegListSMILType::sSingleton);
|
||||
SVGPathDataAndOwner *list = static_cast<SVGPathDataAndOwner*>(val.mU.mPtr);
|
||||
nsresult rv = list->SetValueFromString(aStr);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
list->SetElement(mElement);
|
||||
aValue.Swap(val);
|
||||
}
|
||||
aPreventCachingOfSandwich = PR_FALSE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsSMILValue
|
||||
SVGAnimatedPathSegList::SMILAnimatedPathSegList::GetBaseValue() const
|
||||
{
|
||||
// To benefit from Return Value Optimization and avoid copy constructor calls
|
||||
// due to our use of return-by-value, we must return the exact same object
|
||||
// from ALL return points. This function must only return THIS variable:
|
||||
nsSMILValue val;
|
||||
|
||||
nsSMILValue tmp(&SVGPathSegListSMILType::sSingleton);
|
||||
SVGPathDataAndOwner *list = static_cast<SVGPathDataAndOwner*>(tmp.mU.mPtr);
|
||||
nsresult rv = list->CopyFrom(mVal->mBaseVal);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
list->SetElement(mElement);
|
||||
val.Swap(tmp);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGAnimatedPathSegList::SMILAnimatedPathSegList::SetAnimValue(const nsSMILValue& aValue)
|
||||
{
|
||||
NS_ASSERTION(aValue.mType == &SVGPathSegListSMILType::sSingleton,
|
||||
"Unexpected type to assign animated value");
|
||||
if (aValue.mType == &SVGPathSegListSMILType::sSingleton) {
|
||||
mVal->SetAnimValue(*static_cast<SVGPathDataAndOwner*>(aValue.mU.mPtr),
|
||||
mElement);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SVGAnimatedPathSegList::SMILAnimatedPathSegList::ClearAnimValue()
|
||||
{
|
||||
if (mVal->mAnimVal) {
|
||||
mVal->ClearAnimValue(mElement);
|
||||
}
|
||||
}
|
||||
#endif // MOZ_SMIL
|
||||
|
160
content/svg/content/src/SVGAnimatedPathSegList.h
Normal file
160
content/svg/content/src/SVGAnimatedPathSegList.h
Normal file
@ -0,0 +1,160 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_SVGANIMATEDPATHSEGLIST_H__
|
||||
#define MOZILLA_SVGANIMATEDPATHSEGLIST_H__
|
||||
|
||||
#include "SVGPathData.h"
|
||||
|
||||
class nsSVGElement;
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
#include "nsISMILAttr.h"
|
||||
#endif // MOZ_SMIL
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Class SVGAnimatedPathSegList
|
||||
*
|
||||
* Despite the fact that no SVGAnimatedPathSegList interface or objects exist
|
||||
* in the SVG specification (unlike e.g. SVGAnimated*Length*List), we
|
||||
* nevertheless have this internal class. (Note that there is an
|
||||
* SVGAnimatedPathData interface, but that's quite different to
|
||||
* SVGAnimatedLengthList since it is inherited by elements, as opposed to
|
||||
* elements having members of that type.) The reason that we have this class is
|
||||
* to provide a single locked down point of entry to the SVGPathData objects,
|
||||
* which helps ensure that the DOM wrappers for SVGPathData objects' are always
|
||||
* kept in sync. This is vitally important (see the comment in
|
||||
* DOMSVGPathSegList::InternalListWillChangeTo) and frees consumers from having
|
||||
* to know or worry about wrappers (or forget about them!) for the most part.
|
||||
*/
|
||||
class SVGAnimatedPathSegList
|
||||
{
|
||||
// friends so that they can get write access to mBaseVal and mAnimVal
|
||||
friend class DOMSVGPathSeg;
|
||||
friend class DOMSVGPathSegList;
|
||||
|
||||
public:
|
||||
SVGAnimatedPathSegList() {}
|
||||
|
||||
/**
|
||||
* Because it's so important that mBaseVal and its DOMSVGPathSegList wrapper
|
||||
* (if any) be kept in sync (see the comment in
|
||||
* DOMSVGPathSegList::InternalListWillChangeTo), this method returns a const
|
||||
* reference. Only our friend classes may get mutable references to mBaseVal.
|
||||
*/
|
||||
const SVGPathData& GetBaseValue() const {
|
||||
return mBaseVal;
|
||||
}
|
||||
|
||||
nsresult SetBaseValueString(const nsAString& aValue);
|
||||
|
||||
void ClearBaseValue();
|
||||
|
||||
/**
|
||||
* const! See comment for GetBaseValue!
|
||||
*/
|
||||
const SVGPathData& GetAnimValue() const {
|
||||
return mAnimVal ? *mAnimVal : mBaseVal;
|
||||
}
|
||||
|
||||
nsresult SetAnimValue(const SVGPathData& aValue,
|
||||
nsSVGElement *aElement);
|
||||
|
||||
void ClearAnimValue(nsSVGElement *aElement);
|
||||
|
||||
/**
|
||||
* Needed for correct DOM wrapper construction since GetAnimValue may
|
||||
* actually return the baseVal!
|
||||
*/
|
||||
void *GetBaseValKey() const {
|
||||
return (void*)&mBaseVal;
|
||||
}
|
||||
void *GetAnimValKey() const {
|
||||
return (void*)&mAnimVal;
|
||||
}
|
||||
|
||||
PRBool IsAnimating() const {
|
||||
return !!mAnimVal;
|
||||
}
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
/// Callers own the returned nsISMILAttr
|
||||
nsISMILAttr* ToSMILAttr(nsSVGElement* aElement);
|
||||
#endif // MOZ_SMIL
|
||||
|
||||
private:
|
||||
|
||||
// mAnimVal is a pointer to allow us to determine if we're being animated or
|
||||
// not. Making it a non-pointer member and using mAnimVal.IsEmpty() to check
|
||||
// if we're animating is not an option, since that would break animation *to*
|
||||
// the empty string (<set to="">).
|
||||
|
||||
SVGPathData mBaseVal;
|
||||
nsAutoPtr<SVGPathData> mAnimVal;
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
struct SMILAnimatedPathSegList : public nsISMILAttr
|
||||
{
|
||||
public:
|
||||
SMILAnimatedPathSegList(SVGAnimatedPathSegList* aVal,
|
||||
nsSVGElement* aElement)
|
||||
: mVal(aVal)
|
||||
, mElement(aElement)
|
||||
{}
|
||||
|
||||
// These will stay alive because a nsISMILAttr only lives as long
|
||||
// as the Compositing step, and DOM elements don't get a chance to
|
||||
// die during that.
|
||||
SVGAnimatedPathSegList *mVal;
|
||||
nsSVGElement *mElement;
|
||||
|
||||
// nsISMILAttr methods
|
||||
virtual nsresult ValueFromString(const nsAString& aStr,
|
||||
const nsISMILAnimationElement* aSrcElement,
|
||||
nsSMILValue& aValue,
|
||||
PRBool& aPreventCachingOfSandwich) const;
|
||||
virtual nsSMILValue GetBaseValue() const;
|
||||
virtual void ClearAnimValue();
|
||||
virtual nsresult SetAnimValue(const nsSMILValue& aValue);
|
||||
};
|
||||
#endif // MOZ_SMIL
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_SVGANIMATEDPATHSEGLIST_H__
|
@ -41,7 +41,6 @@
|
||||
#include "SVGMotionSMILType.h"
|
||||
#include "SVGMotionSMILPathUtils.h"
|
||||
#include "nsSVGPathDataParser.h"
|
||||
#include "nsSVGPathSeg.h"
|
||||
#include "nsSVGPathElement.h" // for nsSVGPathList
|
||||
#include "nsSVGMpathElement.h"
|
||||
|
||||
@ -253,13 +252,15 @@ SVGMotionSMILAnimationFunction::
|
||||
// Use the path that's the target of our chosen <mpath> child.
|
||||
nsSVGPathElement* pathElem = aMpathElem->GetReferencedPath();
|
||||
if (pathElem) {
|
||||
const nsAttrValue* value = pathElem->GetParsedAttr(nsGkAtoms::d);
|
||||
if (value) {
|
||||
const nsAString& pathSpec = value->GetStringValue();
|
||||
nsresult rv = SetPathVerticesFromPathString(pathSpec);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
const SVGPathData &path = pathElem->GetAnimPathSegList()->GetAnimValue();
|
||||
// Path data must contain of at least one path segment (if the path data
|
||||
// doesn't begin with a valid "M", then it's invalid).
|
||||
if (path.Length()) {
|
||||
PRBool ok =
|
||||
path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices);
|
||||
if (ok && mPathVertices.Length()) {
|
||||
mPath = pathElem->GetFlattenedPath(
|
||||
pathElem->PrependLocalTransformTo(gfxMatrix()));
|
||||
pathElem->PrependLocalTransformTo(gfxMatrix()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,65 +273,25 @@ SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr()
|
||||
mPathSourceType = ePathSourceType_PathAttr;
|
||||
|
||||
// Generate gfxFlattenedPath from |path| attr
|
||||
nsresult rv;
|
||||
nsSVGPathList pathData;
|
||||
nsSVGPathDataParserToInternal pathParser(&pathData);
|
||||
rv = pathParser.Parse(pathSpec);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Parse error.
|
||||
SVGPathData path;
|
||||
nsSVGPathDataParserToInternal pathParser(&path);
|
||||
|
||||
// We ignore any failure returned from Parse() since the SVG spec says to
|
||||
// accept all segments up to the first invalid token. Instead we must
|
||||
// explicitly check that the parse produces at least one path segment (if
|
||||
// the path data doesn't begin with a valid "M", then it's invalid).
|
||||
pathParser.Parse(pathSpec);
|
||||
if (!path.Length()) {
|
||||
return;
|
||||
}
|
||||
mPath = pathData.GetFlattenedPath(gfxMatrix());
|
||||
|
||||
// Generate list of vertices from |path| attr
|
||||
rv = SetPathVerticesFromPathString(pathSpec);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
// The first parser liked our string, but the second did not. (unexpected,
|
||||
// but possible depending on parser implementations.) Clear path so we
|
||||
// completely (instead of partially) fail.
|
||||
mPath = path.ToFlattenedPath(gfxMatrix());
|
||||
PRBool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices);
|
||||
if (!ok || !mPathVertices.Length()) {
|
||||
mPath = nsnull;
|
||||
NS_WARNING("nsSVGPathDataParserToInternal successfully parsed path string, but "
|
||||
"nsSVGPathDataParserToDOM did not");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGMotionSMILAnimationFunction::SetPathVerticesFromPathString(const nsAString& aPathSpec)
|
||||
{
|
||||
// Parse the string to an array of path segments.
|
||||
nsCOMArray<nsIDOMSVGPathSeg> pathSegments;
|
||||
nsSVGPathDataParserToDOM segmentParser(&pathSegments);
|
||||
nsresult rv = segmentParser.Parse(aPathSpec);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Iterate across the parsed segments to populate our mPathVertices array.
|
||||
PRUint32 numSegments = pathSegments.Count();
|
||||
nsSVGPathSegTraversalState ts;
|
||||
double runningDistTotal = 0.0;
|
||||
for (PRUint32 i = 0; i < numSegments; ++i) {
|
||||
nsSVGPathSeg* segment = static_cast<nsSVGPathSeg*>(pathSegments[i]);
|
||||
|
||||
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
segment->GetPathSegType(&type);
|
||||
if (i == 0 ||
|
||||
(type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
|
||||
type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL)) {
|
||||
|
||||
// Increment running length total (note that MoveTo's have 0 length)
|
||||
runningDistTotal += segment->GetLength(&ts);
|
||||
|
||||
// Add an entry for the current point.
|
||||
if (!mPathVertices.AppendElement(runningDistTotal)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Helper to regenerate our path representation & its list of vertices
|
||||
void
|
||||
SVGMotionSMILAnimationFunction::
|
||||
|
@ -95,7 +95,6 @@ protected:
|
||||
|
||||
// Helpers for GetValues
|
||||
void MarkStaleIfAttributeAffectsPath(nsIAtom* aAttribute);
|
||||
nsresult SetPathVerticesFromPathString(const nsAString& aPathSpec);
|
||||
void RebuildPathAndVertices(const nsIContent* aContextElem);
|
||||
void RebuildPathAndVerticesFromMpathElem(nsSVGMpathElement* aMpathElem);
|
||||
void RebuildPathAndVerticesFromPathAttr();
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "nsSVGAngle.h"
|
||||
#include "nsIDOMSVGAngle.h"
|
||||
#include "nsSVGPathElement.h"
|
||||
#include "nsSVGPathSeg.h"
|
||||
#include "nsIDOMSVGPathSeg.h"
|
||||
#include "nsIDOMSVGPathSegList.h"
|
||||
#include "nsMathUtils.h"
|
||||
|
750
content/svg/content/src/SVGPathData.cpp
Normal file
750
content/svg/content/src/SVGPathData.cpp
Normal file
@ -0,0 +1,750 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "SVGPathData.h"
|
||||
#include "SVGAnimatedPathSegList.h"
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsISVGValueUtils.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "string.h"
|
||||
#include "nsSVGPathDataParser.h"
|
||||
#include "nsSVGPathGeometryElement.h" // for nsSVGMark
|
||||
#include "gfxPlatform.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsresult
|
||||
SVGPathData::CopyFrom(const SVGPathData& rhs)
|
||||
{
|
||||
if (!mData.SetCapacity(rhs.mData.Length())) {
|
||||
// Yes, we do want fallible alloc here
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mData = rhs.mData;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SVGPathData::GetValueAsString(nsAString& aValue) const
|
||||
{
|
||||
// we need this function in DidChangePathSegList
|
||||
aValue.Truncate();
|
||||
if (!Length()) {
|
||||
return;
|
||||
}
|
||||
PRUint32 i = 0;
|
||||
for (;;) {
|
||||
nsAutoString segAsString;
|
||||
SVGPathSegUtils::GetValueAsString(&mData[i], segAsString);
|
||||
// We ignore OOM, since it's not useful for us to return an error.
|
||||
aValue.Append(segAsString);
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
if (i >= mData.Length()) {
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
return;
|
||||
}
|
||||
aValue.Append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathData::SetValueFromString(const nsAString& aValue)
|
||||
{
|
||||
// We don't use a temp variable since the spec says to parse everything up to
|
||||
// the first error. We still return any error though so that callers know if
|
||||
// there's a problem.
|
||||
|
||||
nsSVGPathDataParserToInternal pathParser(this);
|
||||
return pathParser.Parse(aValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathData::AppendSeg(PRUint32 aType, ...)
|
||||
{
|
||||
PRUint32 oldLength = mData.Length();
|
||||
PRUint32 newLength = oldLength + 1 + SVGPathSegUtils::ArgCountForType(aType);
|
||||
if (!mData.SetLength(newLength)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mData[oldLength] = SVGPathSegUtils::EncodeType(aType);
|
||||
va_list args;
|
||||
va_start(args, aType);
|
||||
for (PRUint32 i = oldLength + 1; i < newLength; ++i) {
|
||||
// NOTE! 'float' is promoted to 'double' when passed through '...'!
|
||||
mData[i] = float(va_arg(args, double));
|
||||
}
|
||||
va_end(args);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
float
|
||||
SVGPathData::GetPathLength() const
|
||||
{
|
||||
float length = 0.0;
|
||||
SVGPathTraversalState state;
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
length += SVGPathSegUtils::GetLength(&mData[i], state);
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
PRUint32
|
||||
SVGPathData::CountItems() const
|
||||
{
|
||||
PRUint32 i = 0, count = 0;
|
||||
|
||||
while (i < mData.Length()) {
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
count++;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
PRBool
|
||||
SVGPathData::GetSegmentLengths(nsTArray<double> *aLengths) const
|
||||
{
|
||||
aLengths->Clear();
|
||||
SVGPathTraversalState state;
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
if (!aLengths->AppendElement(SVGPathSegUtils::GetLength(&mData[i], state))) {
|
||||
aLengths->Clear();
|
||||
return PR_FALSE;
|
||||
}
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aOutput) const
|
||||
{
|
||||
double distRunningTotal = 0.0;
|
||||
SVGPathTraversalState state;
|
||||
|
||||
aOutput->Clear();
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
PRUint32 segType = SVGPathSegUtils::DecodeType(mData[i]);
|
||||
|
||||
// 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
|
||||
// the duration...':
|
||||
//
|
||||
// http://www.w3.org/TR/SVG11/animate.html#AnimateMotionElement
|
||||
//
|
||||
// This is important in the non-default case of calcMode="linear". In
|
||||
// this case an equal amount of time is spent on each path segment,
|
||||
// except on moveto segments which are jumped over immediately.
|
||||
|
||||
if (i == 0 || (segType != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
|
||||
segType != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL)) {
|
||||
distRunningTotal += SVGPathSegUtils::GetLength(&mData[i], state);
|
||||
if (!aOutput->AppendElement(distRunningTotal)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(segType);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt?");
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
SVGPathData::GetPathSegAtLength(float aDistance) const
|
||||
{
|
||||
// TODO [SVGWG issue] get specified what happen if 'aDistance' < 0, or
|
||||
// 'aDistance' > the length of the path, or the seg list is empty.
|
||||
// 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) {
|
||||
return segIndex;
|
||||
}
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
segIndex++;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
return NS_MAX(0U, segIndex - 1); // -1 because while loop takes us 1 too far
|
||||
}
|
||||
|
||||
void
|
||||
SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||
{
|
||||
PRUint32 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
gfxPoint pathStart(0.0, 0.0); // start point of [sub]path
|
||||
gfxPoint segEnd(0.0, 0.0); // end point of previous/current segment
|
||||
gfxPoint cp1, cp2; // previous bezier's control points
|
||||
gfxPoint tcp1, tcp2; // temporaries
|
||||
|
||||
// Regarding cp1 and cp2: If the previous segment was a cubic bezier curve,
|
||||
// then cp2 is its second control point. If the previous segment was a
|
||||
// quadratic curve, then cp1 is its (only) control point.
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
segType = SVGPathSegUtils::DecodeType(mData[i++]);
|
||||
|
||||
switch (segType)
|
||||
{
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
|
||||
segEnd = pathStart;
|
||||
aCtx->ClosePath();
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
|
||||
pathStart = segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->MoveTo(segEnd);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
|
||||
pathStart = segEnd += gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->MoveTo(segEnd);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->LineTo(segEnd);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
|
||||
segEnd += gfxPoint(mData[i], mData[i+1]);
|
||||
aCtx->LineTo(segEnd);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
|
||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
||||
cp2 = gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = gfxPoint(mData[i+4], mData[i+5]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
i += 6;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
|
||||
cp1 = segEnd + gfxPoint(mData[i], mData[i+1]);
|
||||
cp2 = segEnd + gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd += gfxPoint(mData[i+4], mData[i+5]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
i += 6;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
||||
// Convert quadratic curve to cubic curve:
|
||||
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]); // changed before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
cp1 = segEnd + gfxPoint(mData[i], mData[i+1]);
|
||||
// Convert quadratic curve to cubic curve:
|
||||
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
|
||||
segEnd += gfxPoint(mData[i+2], mData[i+3]); // changed before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
|
||||
{
|
||||
gfxPoint radii(mData[i], mData[i+1]);
|
||||
gfxPoint start = segEnd;
|
||||
gfxPoint end = gfxPoint(mData[i+5], mData[i+6]);
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) {
|
||||
end += start;
|
||||
}
|
||||
segEnd = end;
|
||||
if (start != end) {
|
||||
if (radii.x == 0.0f || radii.y == 0.0f) {
|
||||
aCtx->LineTo(end);
|
||||
i += 7;
|
||||
break;
|
||||
}
|
||||
nsSVGArcConverter converter(start, end, radii, mData[i+2],
|
||||
mData[i+3] != 0, mData[i+4] != 0);
|
||||
while (converter.GetNextSegment(&cp1, &cp2, &end)) {
|
||||
aCtx->CurveTo(cp1, cp2, end);
|
||||
}
|
||||
}
|
||||
i += 7;
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||
segEnd = gfxPoint(mData[i++], segEnd.y);
|
||||
aCtx->LineTo(segEnd);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
|
||||
segEnd += gfxPoint(mData[i++], 0.0f);
|
||||
aCtx->LineTo(segEnd);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
|
||||
segEnd = gfxPoint(segEnd.x, mData[i++]);
|
||||
aCtx->LineTo(segEnd);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
|
||||
segEnd += gfxPoint(0.0f, mData[i++]);
|
||||
aCtx->LineTo(segEnd);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segEnd * 2 - cp2 : segEnd;
|
||||
cp2 = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segEnd * 2 - cp2 : segEnd;
|
||||
cp2 = segEnd + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd += gfxPoint(mData[i+2], mData[i+3]);
|
||||
aCtx->CurveTo(cp1, cp2, segEnd);
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segEnd * 2 - cp1 : segEnd;
|
||||
// Convert quadratic curve to cubic curve:
|
||||
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segEnd * 2 - cp1 : segEnd;
|
||||
// Convert quadratic curve to cubic curve:
|
||||
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3;
|
||||
segEnd = segEnd + gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2!
|
||||
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||
aCtx->CurveTo(tcp1, tcp2, segEnd);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("Bad path segment type");
|
||||
return; // according to spec we'd use everything up to the bad seg anyway
|
||||
}
|
||||
prevSegType = segType;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFlattenedPath>
|
||||
SVGPathData::ToFlattenedPath(const gfxMatrix& aMatrix) const
|
||||
{
|
||||
nsRefPtr<gfxContext> ctx =
|
||||
new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
|
||||
|
||||
ctx->SetMatrix(aMatrix);
|
||||
ConstructPath(ctx);
|
||||
ctx->IdentityMatrix();
|
||||
|
||||
return ctx->GetFlattenedPath();
|
||||
}
|
||||
|
||||
static PRBool IsMoveto(PRUint16 aSegType)
|
||||
{
|
||||
return aSegType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS ||
|
||||
aSegType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL;
|
||||
}
|
||||
|
||||
static float AngleOfVector(gfxPoint v)
|
||||
{
|
||||
// C99 says about atan2 "A domain error may occur if both arguments are
|
||||
// zero" and "On a domain error, the function returns an implementation-
|
||||
// defined value". In the case of atan2 the implementation-defined value
|
||||
// seems to commonly be zero, but it could just as easily be a NaN value.
|
||||
// We specifically want zero in this case, hence the check:
|
||||
|
||||
return (v != gfxPoint(0.0f, 0.0f)) ? atan2(v.y, v.x) : 0.0f;
|
||||
}
|
||||
|
||||
// TODO replace callers with calls to AngleOfVector
|
||||
static double
|
||||
CalcVectorAngle(double ux, double uy, double vx, double vy)
|
||||
{
|
||||
double ta = atan2(uy, ux);
|
||||
double tb = atan2(vy, vx);
|
||||
if (tb >= ta)
|
||||
return tb-ta;
|
||||
return 2 * M_PI - (ta-tb);
|
||||
}
|
||||
|
||||
void
|
||||
SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||
{
|
||||
// This code should assume that ANY type of segment can appear at ANY index.
|
||||
// It should also assume that segments such as M and Z can appear in weird
|
||||
// places, and repeat multiple times consecutively.
|
||||
|
||||
gfxPoint pathStart, segStart, segEnd;
|
||||
gfxPoint cp1, cp2; // control points for current bezier curve
|
||||
gfxPoint prevCP; // last control point of previous bezier curve
|
||||
|
||||
PRUint16 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
|
||||
// info on the current [sub]path (reset every M command):
|
||||
gfxPoint pathStartPoint(0, 0);
|
||||
float pathStartAngle = 0;
|
||||
|
||||
float prevSegEndAngle = 0, segStartAngle = 0, segEndAngle = 0;
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
segType = SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args
|
||||
|
||||
switch (segType) // to find segStartAngle, segEnd and segEndAngle
|
||||
{
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
|
||||
segEnd = pathStart;
|
||||
segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS) {
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
}
|
||||
pathStart = segEnd;
|
||||
// If authors are going to specify multiple consecutive moveto commands
|
||||
// with markers, me might as well make the angle do something useful:
|
||||
segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS) {
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
}
|
||||
segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS) {
|
||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
||||
cp2 = gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = gfxPoint(mData[i+4], mData[i+5]);
|
||||
} else {
|
||||
cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
cp2 = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = segStart + gfxPoint(mData[i+4], mData[i+5]);
|
||||
}
|
||||
prevCP = cp2;
|
||||
if (cp1 == segStart) {
|
||||
cp1 = cp2;
|
||||
}
|
||||
if (cp2 == segEnd) {
|
||||
cp2 = cp1;
|
||||
}
|
||||
segStartAngle = AngleOfVector(cp1 - segStart);
|
||||
segEndAngle = AngleOfVector(segEnd - cp2);
|
||||
i += 6;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) {
|
||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
||||
} else {
|
||||
cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
}
|
||||
prevCP = cp1;
|
||||
segStartAngle = AngleOfVector(cp1 - segStart);
|
||||
segEndAngle = AngleOfVector(segEnd - cp1);
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
|
||||
{
|
||||
float rx = mData[i];
|
||||
float ry = mData[i+1];
|
||||
float angle = mData[i+2];
|
||||
PRBool largeArcFlag = mData[i+3] != 0.0f;
|
||||
PRBool sweepFlag = mData[i+4] != 0.0f;
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) {
|
||||
segEnd = gfxPoint(mData[i+5], mData[i+6]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i+5], mData[i+6]);
|
||||
}
|
||||
|
||||
// See section F.6 of SVG 1.1 for details on what we're doing here:
|
||||
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
|
||||
|
||||
if (segStart == segEnd) {
|
||||
// F.6.2 says "If the endpoints (x1, y1) and (x2, y2) are identical,
|
||||
// then this is equivalent to omitting the elliptical arc segment
|
||||
// entirely." We take that very literally here, not adding a mark, and
|
||||
// not even setting any of the 'prev' variables so that it's as if this
|
||||
// arc had never existed; note the difference this will make e.g. if
|
||||
// the arc is proceeded by a bezier curve and followed by a "smooth"
|
||||
// bezier curve of the same degree!
|
||||
i += 7;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Below we have funny interleaving of F.6.6 (Correction of out-of-range
|
||||
// radii) and F.6.5 (Conversion from endpoint to center parameterization)
|
||||
// which is designed to avoid some unnecessary calculations.
|
||||
|
||||
if (rx == 0.0 || ry == 0.0) {
|
||||
// F.6.6 step 1 - straight line or coincidental points
|
||||
segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
|
||||
i += 7;
|
||||
break;
|
||||
}
|
||||
rx = fabs(rx); // F.6.6.1
|
||||
ry = fabs(ry);
|
||||
|
||||
// F.6.5.1:
|
||||
angle = angle * M_PI/180.0;
|
||||
float x1p = cos(angle) * (segStart.x - segEnd.x) / 2.0
|
||||
+ sin(angle) * (segStart.y - segEnd.y) / 2.0;
|
||||
float y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0
|
||||
+ cos(angle) *(segStart.y - segEnd.y) / 2.0;
|
||||
|
||||
// This is the root in F.6.5.2 and the numerator under that root:
|
||||
float root;
|
||||
float numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
|
||||
|
||||
if (numerator < 0.0) {
|
||||
// F.6.6 step 3 - |numerator < 0.0| is equivalent to the result of
|
||||
// F.6.6.2 (lamedh) being greater than one. What we have here is radii
|
||||
// that do not reach between segStart and segEnd, so we need to correct
|
||||
// them.
|
||||
float lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2
|
||||
float s = sqrt(lamedh);
|
||||
rx *= s; // F.6.6.3
|
||||
ry *= s;
|
||||
// rx and ry changed, so we have to recompute numerator
|
||||
numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
|
||||
NS_ABORT_IF_FALSE(numerator >= 0,
|
||||
"F.6.6.3 should prevent this. Will sqrt(-num)!");
|
||||
}
|
||||
root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p));
|
||||
if (largeArcFlag == sweepFlag)
|
||||
root = -root;
|
||||
|
||||
float cxp = root * rx * y1p / ry; // F.6.5.2
|
||||
float cyp = -root * ry * x1p / rx;
|
||||
|
||||
float theta, delta;
|
||||
theta = CalcVectorAngle(1.0, 0.0, (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5
|
||||
delta = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry,
|
||||
(-x1p-cxp)/rx, (-y1p-cyp)/ry); // F.6.5.6
|
||||
if (!sweepFlag && delta > 0)
|
||||
delta -= 2.0 * M_PI;
|
||||
else if (sweepFlag && delta < 0)
|
||||
delta += 2.0 * M_PI;
|
||||
|
||||
float tx1, ty1, tx2, ty2;
|
||||
tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta);
|
||||
ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta);
|
||||
tx2 = -cos(angle)*rx*sin(theta+delta) - sin(angle)*ry*cos(theta+delta);
|
||||
ty2 = -sin(angle)*rx*sin(theta+delta) + cos(angle)*ry*cos(theta+delta);
|
||||
|
||||
if (delta < 0.0f) {
|
||||
tx1 = -tx1;
|
||||
ty1 = -ty1;
|
||||
tx2 = -tx2;
|
||||
ty2 = -ty2;
|
||||
}
|
||||
|
||||
segStartAngle = atan2(ty1, tx1);
|
||||
segEndAngle = atan2(ty2, tx2);
|
||||
i += 7;
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS) {
|
||||
segEnd = gfxPoint(mData[i++], segStart.y);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i++], 0.0f);
|
||||
}
|
||||
segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS) {
|
||||
segEnd = gfxPoint(segStart.x, mData[i++]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(0.0f, mData[i++]);
|
||||
}
|
||||
segStartAngle = segEndAngle = AngleOfVector(segEnd - segStart);
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - prevCP : segStart;
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) {
|
||||
cp2 = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
||||
} else {
|
||||
cp2 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
}
|
||||
prevCP = cp2;
|
||||
if (cp1 == segStart) {
|
||||
cp1 = cp2;
|
||||
}
|
||||
if (cp2 == segEnd) {
|
||||
cp2 = cp1;
|
||||
}
|
||||
segStartAngle = AngleOfVector(cp1 - segStart);
|
||||
segEndAngle = AngleOfVector(segEnd - cp2);
|
||||
i += 4;
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - prevCP : segStart;
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) {
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
}
|
||||
prevCP = cp1;
|
||||
segStartAngle = AngleOfVector(cp1 - segStart);
|
||||
segEndAngle = AngleOfVector(segEnd - cp1);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Leave any existing marks in aMarks so we have a visual indication of
|
||||
// when things went wrong.
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "Unknown segment type - path corruption?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the angle of the mark at the start of this segment:
|
||||
if (aMarks->Length()) {
|
||||
nsSVGMark &mark = aMarks->ElementAt(aMarks->Length() - 1);
|
||||
if (!IsMoveto(segType) && IsMoveto(prevSegType)) {
|
||||
// start of new subpath
|
||||
pathStartAngle = mark.angle = segStartAngle;
|
||||
} else if (IsMoveto(segType) && !IsMoveto(prevSegType)) {
|
||||
// end of a subpath
|
||||
if (prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)
|
||||
mark.angle = prevSegEndAngle;
|
||||
} else {
|
||||
if (!(segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
|
||||
prevSegType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH))
|
||||
mark.angle = nsSVGUtils::AngleBisect(prevSegEndAngle, segStartAngle);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the mark at the end of this segment, and set its position:
|
||||
if (!aMarks->AppendElement(nsSVGMark(segEnd.x, segEnd.y, 0))) {
|
||||
aMarks->Clear(); // OOM, so try to free some
|
||||
return;
|
||||
}
|
||||
|
||||
if (segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
|
||||
prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) {
|
||||
aMarks->ElementAt(aMarks->Length() - 1).angle =
|
||||
//aMarks->ElementAt(pathStartIndex).angle =
|
||||
nsSVGUtils::AngleBisect(segEndAngle, pathStartAngle);
|
||||
}
|
||||
|
||||
prevSegType = segType;
|
||||
prevSegEndAngle = segEndAngle;
|
||||
segStart = segEnd;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
if (aMarks->Length() &&
|
||||
prevSegType != nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)
|
||||
aMarks->ElementAt(aMarks->Length() - 1).angle = prevSegEndAngle;
|
||||
}
|
||||
|
284
content/svg/content/src/SVGPathData.h
Normal file
284
content/svg/content/src/SVGPathData.h
Normal file
@ -0,0 +1,284 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_SVGPATHDATA_H__
|
||||
#define MOZILLA_SVGPATHDATA_H__
|
||||
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsSVGElement.h"
|
||||
|
||||
class gfxContext;
|
||||
class gfxMatrix;
|
||||
class gfxFlattenedPath;
|
||||
class nsSVGPathDataParserToInternal;
|
||||
struct nsSVGMark;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* ATTENTION! WARNING! WATCH OUT!!
|
||||
*
|
||||
* Consumers that modify objects of this type absolutely MUST keep the DOM
|
||||
* wrappers for those lists (if any) in sync!! That's why this class is so
|
||||
* locked down.
|
||||
*
|
||||
* The DOM wrapper class for this class is DOMSVGPathSegList.
|
||||
*
|
||||
* This class is not called |class SVGPathSegList| for one very good reason;
|
||||
* this class does not provide a list of "SVGPathSeg" items, it provides an
|
||||
* array of floats into which path segments are encoded. See the paragraphs
|
||||
* that follow for why. Note that the Length() method returns the number of
|
||||
* floats in our array, not the number of encoded segments, and the index
|
||||
* operator indexes floats in the array, not segments. If this class were
|
||||
* called SVGPathSegList the names of these methods would be very misleading.
|
||||
*
|
||||
* The reason this class is designed in this way is because there are many
|
||||
* different types of path segment, each taking a different numbers of
|
||||
* arguments. We want to store the segments in an nsTArray to avoid individual
|
||||
* allocations for each item, but the different size of segments means we can't
|
||||
* have one single segment type for the nsTArray (not without using a space
|
||||
* wasteful union or something similar). Since the internal code does not need
|
||||
* to index into the list (the DOM wrapper does, but it handles that itself)
|
||||
* the obvious solution is to have the items in this class take up variable
|
||||
* width and have the internal code iterate over these lists rather than index
|
||||
* into them.
|
||||
*
|
||||
* Implementing indexing to segments with O(1) performance would require us to
|
||||
* allocate and maintain a separate segment index table (keeping that table in
|
||||
* sync when items are inserted or removed from the list). So long as the
|
||||
* internal code doesn't require indexing to segments, we can avoid that
|
||||
* overhead and additional complexity.
|
||||
*
|
||||
* Segment encoding: the first float in the encoding of a segment contains the
|
||||
* segment's type. The segment's type is encoded to/decoded from this float
|
||||
* using the static methods SVGPathSegUtils::EncodeType(PRUint32)/
|
||||
* SVGPathSegUtils::DecodeType(float). If the path segment type in question
|
||||
* takes any arguments then these follow the first float, and are in the same
|
||||
* order as they are given in a <path> element's 'd' attribute (NOT in the
|
||||
* order of the createSVGPathSegXxx() methods' arguments from the SVG DOM
|
||||
* interface SVGPathElement, which are different...grr). Consumers can use
|
||||
* SVGPathSegUtils::ArgCountForType(type) to determine how many arguments
|
||||
* there are (if any), and thus where the current encoded segment ends, and
|
||||
* where the next segment (if any) begins.
|
||||
*/
|
||||
class SVGPathData
|
||||
{
|
||||
friend class SVGAnimatedPathSegList;
|
||||
friend class DOMSVGPathSegList;
|
||||
friend class DOMSVGPathSeg;
|
||||
friend class ::nsSVGPathDataParserToInternal;
|
||||
// nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers
|
||||
// are responsible for that!
|
||||
|
||||
public:
|
||||
|
||||
SVGPathData(){}
|
||||
~SVGPathData(){}
|
||||
|
||||
// Only methods that don't make/permit modification to this list are public.
|
||||
// Only our friend classes can access methods that may change us.
|
||||
|
||||
/// This may return an incomplete string on OOM, but that's acceptable.
|
||||
void GetValueAsString(nsAString& aValue) const;
|
||||
|
||||
PRBool IsEmpty() const {
|
||||
return mData.IsEmpty();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* This method iterates over the encoded segment data and countes the number
|
||||
* of segments we currently have.
|
||||
*/
|
||||
PRUint32 CountItems() const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the number of *floats* in the encoding array, and NOT the number
|
||||
* of segments encoded in this object. (For that, see CountItems() above.)
|
||||
*/
|
||||
PRUint32 Length() const {
|
||||
return mData.Length();
|
||||
}
|
||||
|
||||
const float& operator[](PRUint32 aIndex) const {
|
||||
return mData[aIndex];
|
||||
}
|
||||
|
||||
// Used by nsSMILCompositor to check if the cached base val is out of date
|
||||
PRBool operator==(const SVGPathData& rhs) const {
|
||||
// We use memcmp so that we don't need to worry that the data encoded in
|
||||
// the first float may have the same bit pattern as a NaN.
|
||||
return mData.Length() == rhs.mData.Length() &&
|
||||
memcmp(mData.Elements(), rhs.mData.Elements(),
|
||||
mData.Length() * sizeof(float)) == 0;
|
||||
}
|
||||
|
||||
PRBool SetCapacity(PRUint32 aSize) {
|
||||
return mData.SetCapacity(aSize);
|
||||
}
|
||||
|
||||
void Compact() {
|
||||
mData.Compact();
|
||||
}
|
||||
|
||||
|
||||
float GetPathLength() const;
|
||||
|
||||
PRUint32 GetPathSegAtLength(float aLength) const;
|
||||
|
||||
void GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const;
|
||||
|
||||
/**
|
||||
* Returns PR_TRUE, except on OOM, in which case returns PR_FALSE.
|
||||
*/
|
||||
PRBool GetSegmentLengths(nsTArray<double> *aLengths) const;
|
||||
|
||||
/**
|
||||
* Returns PR_TRUE, except on OOM, in which case returns PR_FALSE.
|
||||
*/
|
||||
PRBool GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aArray) const;
|
||||
|
||||
already_AddRefed<gfxFlattenedPath>
|
||||
ToFlattenedPath(const gfxMatrix& aMatrix) const;
|
||||
|
||||
void ConstructPath(gfxContext *aCtx) const;
|
||||
|
||||
// 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.
|
||||
// If you need wider access to these methods, consider adding a method to
|
||||
// SVGAnimatedPathSegList and having that class act as an intermediary so it
|
||||
// can take care of keeping DOM wrappers in sync.
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* This may fail on OOM if the internal capacity needs to be increased, in
|
||||
* which case the list will be left unmodified.
|
||||
*/
|
||||
nsresult CopyFrom(const SVGPathData& rhs);
|
||||
|
||||
float& operator[](PRUint32 aIndex) {
|
||||
return mData[aIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* This may fail (return PR_FALSE) on OOM if the internal capacity is being
|
||||
* increased, in which case the list will be left unmodified.
|
||||
*/
|
||||
PRBool SetLength(PRUint32 aLength) {
|
||||
return mData.SetLength(aLength);
|
||||
}
|
||||
|
||||
nsresult SetValueFromString(const nsAString& aValue);
|
||||
|
||||
void Clear() {
|
||||
mData.Clear();
|
||||
}
|
||||
|
||||
// Our DOM wrappers have direct access to our mData, so they directly
|
||||
// manipulate it rather than us implementing:
|
||||
//
|
||||
// * InsertItem(PRUint32 aDataIndex, PRUint32 aType, const float *aArgs);
|
||||
// * ReplaceItem(PRUint32 aDataIndex, PRUint32 aType, const float *aArgs);
|
||||
// * RemoveItem(PRUint32 aDataIndex);
|
||||
// * PRBool AppendItem(PRUint32 aType, const float *aArgs);
|
||||
|
||||
nsresult AppendSeg(PRUint32 aType, ...); // variable number of float args
|
||||
|
||||
nsTArray<float> mData;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This SVGPathData subclass is for SVGPathSegListSMILType which needs to
|
||||
* have write access to the lists it works with.
|
||||
*
|
||||
* Instances of this class do not have DOM wrappers that need to be kept in
|
||||
* sync, so we can safely expose any protected base class methods required by
|
||||
* the SMIL code.
|
||||
*/
|
||||
class SVGPathDataAndOwner : public SVGPathData
|
||||
{
|
||||
public:
|
||||
|
||||
SVGPathDataAndOwner(nsSVGElement *aElement = nsnull)
|
||||
: mElement(aElement)
|
||||
{}
|
||||
|
||||
void SetElement(nsSVGElement *aElement) {
|
||||
mElement = aElement;
|
||||
}
|
||||
|
||||
nsSVGElement* Element() const {
|
||||
return mElement;
|
||||
}
|
||||
|
||||
nsresult CopyFrom(const SVGPathDataAndOwner& rhs) {
|
||||
mElement = rhs.mElement;
|
||||
return SVGPathData::CopyFrom(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposed so that SVGPathData baseVals can be copied to
|
||||
* 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);
|
||||
}
|
||||
|
||||
private:
|
||||
// We must keep a strong reference to our element because we may belong to a
|
||||
// cached baseVal nsSMILValue. See the comments starting at:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
|
||||
nsRefPtr<nsSVGElement> mElement;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_SVGPATHDATA_H__
|
242
content/svg/content/src/SVGPathSegListSMILType.cpp
Normal file
242
content/svg/content/src/SVGPathSegListSMILType.cpp
Normal file
@ -0,0 +1,242 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Mozilla SVG project.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "SVGPathSegListSMILType.h"
|
||||
#include "nsSMILValue.h"
|
||||
#include "SVGPathData.h"
|
||||
#include <math.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/*static*/ SVGPathSegListSMILType SVGPathSegListSMILType::sSingleton;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISMILType implementation
|
||||
|
||||
void
|
||||
SVGPathSegListSMILType::Init(nsSMILValue &aValue) const
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
|
||||
aValue.mU.mPtr = new SVGPathDataAndOwner();
|
||||
aValue.mType = this;
|
||||
}
|
||||
|
||||
void
|
||||
SVGPathSegListSMILType::Destroy(nsSMILValue& aValue) const
|
||||
{
|
||||
NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
|
||||
delete static_cast<SVGPathDataAndOwner*>(aValue.mU.mPtr);
|
||||
aValue.mU.mPtr = nsnull;
|
||||
aValue.mType = &nsSMILNullType::sSingleton;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathSegListSMILType::Assign(nsSMILValue& aDest,
|
||||
const nsSMILValue& aSrc) const
|
||||
{
|
||||
NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
|
||||
NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
|
||||
|
||||
const SVGPathDataAndOwner* src =
|
||||
static_cast<const SVGPathDataAndOwner*>(aSrc.mU.mPtr);
|
||||
SVGPathDataAndOwner* dest =
|
||||
static_cast<SVGPathDataAndOwner*>(aDest.mU.mPtr);
|
||||
|
||||
return dest->CopyFrom(*src);
|
||||
}
|
||||
|
||||
PRBool
|
||||
SVGPathSegListSMILType::IsEqual(const nsSMILValue& aLeft,
|
||||
const nsSMILValue& aRight) const
|
||||
{
|
||||
NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
|
||||
NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
|
||||
|
||||
return *static_cast<const SVGPathDataAndOwner*>(aLeft.mU.mPtr) ==
|
||||
*static_cast<const SVGPathDataAndOwner*>(aRight.mU.mPtr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathSegListSMILType::Add(nsSMILValue& aDest,
|
||||
const nsSMILValue& aValueToAdd,
|
||||
PRUint32 aCount) const
|
||||
{
|
||||
NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
|
||||
NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
|
||||
|
||||
SVGPathDataAndOwner& dest =
|
||||
*static_cast<SVGPathDataAndOwner*>(aDest.mU.mPtr);
|
||||
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
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == dest.Length(), "Very, very bad - path data corrupt");
|
||||
|
||||
// For now we only support pure 'to' animation.
|
||||
// nsSVGUtils::ReportToConsole
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathSegListSMILType::ComputeDistance(const nsSMILValue& aFrom,
|
||||
const nsSMILValue& aTo,
|
||||
double& aDistance) const
|
||||
{
|
||||
NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
|
||||
NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
|
||||
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=522306#c18
|
||||
|
||||
// nsSVGUtils::ReportToConsole
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathSegListSMILType::Interpolate(const nsSMILValue& aStartVal,
|
||||
const nsSMILValue& aEndVal,
|
||||
double aUnitDistance,
|
||||
nsSMILValue& aResult) const
|
||||
{
|
||||
NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
|
||||
"Trying to interpolate different types");
|
||||
NS_PRECONDITION(aStartVal.mType == this,
|
||||
"Unexpected types for interpolation");
|
||||
NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
|
||||
|
||||
const SVGPathDataAndOwner& start =
|
||||
*static_cast<const SVGPathDataAndOwner*>(aStartVal.mU.mPtr);
|
||||
const SVGPathDataAndOwner& end =
|
||||
*static_cast<const SVGPathDataAndOwner*>(aEndVal.mU.mPtr);
|
||||
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
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!result.SetLength(end.Length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
PRUint32 i = 0;
|
||||
|
||||
if (start.Length() == 0) { // identity path
|
||||
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)) {
|
||||
result[i] = end[i] * aUnitDistance;
|
||||
result[i+1] = end[i+1] * aUnitDistance;
|
||||
result[i+2] = end[i+2] * aUnitDistance;
|
||||
// boolean args largeArcFlag and sweepFlag must be the same
|
||||
result[i+3] = end[i+3];
|
||||
result[i+4] = end[i+4];
|
||||
result[i+5] = end[i+5] * aUnitDistance;
|
||||
result[i+6] = end[i+6] * aUnitDistance;
|
||||
i = segEnd;
|
||||
} else {
|
||||
for (; i < segEnd; ++i) {
|
||||
result[i] = end[i] * aUnitDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == end.Length(), "Very, very bad - path data corrupt");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
86
content/svg/content/src/SVGPathSegListSMILType.h
Normal file
86
content/svg/content/src/SVGPathSegListSMILType.h
Normal file
@ -0,0 +1,86 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Mozilla SVG project.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
|
||||
#define MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
|
||||
|
||||
#include "nsISMILType.h"
|
||||
|
||||
class nsSMILValue;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// SVGPathSegListSMILType
|
||||
//
|
||||
// Operations for animating an SVGPathData.
|
||||
//
|
||||
class SVGPathSegListSMILType : public nsISMILType
|
||||
{
|
||||
public:
|
||||
// Singleton for nsSMILValue objects to hold onto.
|
||||
static SVGPathSegListSMILType sSingleton;
|
||||
|
||||
protected:
|
||||
// nsISMILType Methods
|
||||
// -------------------
|
||||
|
||||
virtual void Init(nsSMILValue& aValue) const;
|
||||
|
||||
virtual void Destroy(nsSMILValue& aValue) const;
|
||||
virtual nsresult Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const;
|
||||
virtual PRBool IsEqual(const nsSMILValue& aLeft,
|
||||
const nsSMILValue& aRight) const;
|
||||
virtual nsresult Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
|
||||
PRUint32 aCount) const;
|
||||
virtual nsresult ComputeDistance(const nsSMILValue& aFrom,
|
||||
const nsSMILValue& aTo,
|
||||
double& aDistance) const;
|
||||
virtual nsresult Interpolate(const nsSMILValue& aStartVal,
|
||||
const nsSMILValue& aEndVal,
|
||||
double aUnitDistance,
|
||||
nsSMILValue& aResult) const;
|
||||
|
||||
private:
|
||||
// Private constructor & destructor: prevent instances beyond my singleton,
|
||||
// and prevent others from deleting my singleton.
|
||||
SVGPathSegListSMILType() {}
|
||||
~SVGPathSegListSMILType() {}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_SVGPATHSEGLISTSMILTYPE_H_
|
461
content/svg/content/src/SVGPathSegUtils.cpp
Normal file
461
content/svg/content/src/SVGPathSegUtils.cpp
Normal file
@ -0,0 +1,461 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsSVGSVGElement.h"
|
||||
#include "nsSVGPathDataParser.h"
|
||||
#include "nsString.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsTextFormatter.h"
|
||||
#include "prdtoa.h"
|
||||
#include <limits>
|
||||
#include "nsMathUtils.h"
|
||||
#include "prtypes.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static const float PATH_SEG_LENGTH_TOLERANCE = 0.0000001f;
|
||||
static const PRUint32 MAX_RECURSION = 10;
|
||||
|
||||
|
||||
/* static */ void
|
||||
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_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) {
|
||||
PRBool largeArcFlag = aSeg[4] != 0.0f;
|
||||
PRBool sweepFlag = aSeg[5] != 0.0f;
|
||||
nsTextFormatter::ssprintf(aValue,
|
||||
NS_LITERAL_STRING("%c%g,%g %g %d,%d %g,%g").get(),
|
||||
typeAsChar, aSeg[1], aSeg[2], aSeg[3],
|
||||
largeArcFlag, sweepFlag, aSeg[6], aSeg[7]);
|
||||
} else {
|
||||
|
||||
switch (ArgCountForType(type)) {
|
||||
case 0:
|
||||
aValue = typeAsChar;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
nsTextFormatter::ssprintf(aValue, NS_LITERAL_STRING("%c%g").get(),
|
||||
typeAsChar, aSeg[1]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
nsTextFormatter::ssprintf(aValue, NS_LITERAL_STRING("%c%g,%g").get(),
|
||||
typeAsChar, aSeg[1], aSeg[2]);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
nsTextFormatter::ssprintf(aValue, NS_LITERAL_STRING("%c%g,%g %g,%g").get(),
|
||||
typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4]);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
nsTextFormatter::ssprintf(aValue,
|
||||
NS_LITERAL_STRING("%c%g,%g %g,%g %g,%g").get(),
|
||||
typeAsChar, aSeg[1], aSeg[2], aSeg[3], aSeg[4],
|
||||
aSeg[5], aSeg[6]);
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "Unknown segment type");
|
||||
aValue = NS_LITERAL_STRING("<unknown-segment-type>").get();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// nsTextFormatter::ssprintf is one of the nsTextFormatter methods that
|
||||
// randomly appends '\0' to its output string, which means that the length
|
||||
// of the output string is one too long. We need to manually remove that '\0'
|
||||
// until nsTextFormatter is fixed.
|
||||
//
|
||||
if (aValue[aValue.Length() - 1] == PRUnichar('\0')) {
|
||||
aValue.SetLength(aValue.Length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static float
|
||||
CalcDistanceBetweenPoints(const gfxPoint &p1, const gfxPoint &p2)
|
||||
{
|
||||
return NS_hypot(p2.x - p1.x, p2.y - p1.y);
|
||||
}
|
||||
|
||||
|
||||
static void SplitQuadraticBezier(const gfxPoint *curve,
|
||||
gfxPoint *left,
|
||||
gfxPoint *right)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static void SplitCubicBezier(const gfxPoint *curve,
|
||||
gfxPoint *left,
|
||||
gfxPoint *right)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static gfxFloat CalcBezLengthHelper(gfxPoint *curve, PRUint32 numPts,
|
||||
PRUint32 recursion_count,
|
||||
void (*split)(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]);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static inline gfxFloat
|
||||
CalcLengthOfCubicBezier(const gfxPoint &pos, const gfxPoint &cp1,
|
||||
const gfxPoint &cp2, const gfxPoint &to)
|
||||
{
|
||||
gfxPoint curve[4] = { pos, cp1, cp2, to };
|
||||
return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
|
||||
}
|
||||
|
||||
static inline gfxFloat
|
||||
CalcLengthOfQuadraticBezier(const gfxPoint &pos, const gfxPoint &cp,
|
||||
const gfxPoint &to)
|
||||
{
|
||||
gfxPoint curve[3] = { pos, cp, to };
|
||||
return CalcBezLengthHelper(curve, 3, 0, SplitQuadraticBezier);
|
||||
}
|
||||
|
||||
|
||||
static float GetLengthOfClosePath(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
float dist = CalcDistanceBetweenPoints(aState.pos, aState.start);
|
||||
aState.pos = aState.cp1 = aState.cp2 = aState.start;
|
||||
return dist;
|
||||
}
|
||||
|
||||
static float GetLengthOfMovetoAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
aState.start = aState.pos = aState.cp1 = aState.cp2 = gfxPoint(aArgs[0], aArgs[1]);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static float GetLengthOfMovetoRel(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;
|
||||
}
|
||||
|
||||
static float GetLengthOfLinetoAbs(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;
|
||||
}
|
||||
|
||||
static float GetLengthOfLinetoRel(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoHorizontalAbs(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoHorizontalRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
aState.cp1 = aState.cp2 = aState.pos += gfxPoint(aArgs[0], 0.0);
|
||||
return fabs(aArgs[0]);
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoVerticalAbs(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoVerticalRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
aState.cp1 = aState.cp2 = aState.pos += gfxPoint(0.0, aArgs[0]);
|
||||
return fabs(aArgs[0]);
|
||||
}
|
||||
|
||||
static float GetLengthOfCurvetoCubicAbs(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoCubicSmoothAbs(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoCubicRel(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoCubicSmoothRel(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoQuadraticSmoothRel(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;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfArcAbs(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];
|
||||
}
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfArcRel(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];
|
||||
}
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
}
|
||||
|
||||
|
||||
typedef float (*getLengthFunc)(const float*, SVGPathTraversalState&);
|
||||
|
||||
/* static */ float
|
||||
SVGPathSegUtils::GetLength(const float *seg, 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);
|
||||
}
|
||||
|
217
content/svg/content/src/SVGPathSegUtils.h
Normal file
217
content/svg/content/src/SVGPathSegUtils.h
Normal file
@ -0,0 +1,217 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla SVG Project code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef MOZILLA_SVGPATHSEGUTILS_H__
|
||||
#define MOZILLA_SVGPATHSEGUTILS_H__
|
||||
|
||||
#include "nsIDOMSVGPathSeg.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsAString.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "gfxPoint.h"
|
||||
|
||||
#define NS_SVG_PATH_SEG_MAX_ARGS 7
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* Code that works with path segments can use an instance of this class to
|
||||
* store/provide information about the start of the current subpath and the
|
||||
* last path segment (if any).
|
||||
*/
|
||||
struct SVGPathTraversalState
|
||||
{
|
||||
SVGPathTraversalState()
|
||||
: start(0.0, 0.0)
|
||||
, pos(0.0, 0.0)
|
||||
, cp1(0.0, 0.0)
|
||||
, cp2(0.0, 0.0)
|
||||
{}
|
||||
|
||||
gfxPoint start; // start point of current sub path (reset each moveto)
|
||||
|
||||
gfxPoint pos; // current position (end point of previous segment)
|
||||
|
||||
gfxPoint cp1; // quadratic control point - if the previous segment was a
|
||||
// quadratic bezier curve then this is set to the absolute
|
||||
// position of its control point, otherwise its set to pos
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class is just a collection of static methods - it doesn't have any data
|
||||
* members, and it's not possible to create instances of this class. This class
|
||||
* exists purely as a convenient place to gather together a bunch of methods
|
||||
* related to manipulating and answering questions about path segments.
|
||||
* Internally we represent path segments purely as an array of floats. See the
|
||||
* comment documenting SVGPathData for more info on that.
|
||||
*
|
||||
* The DOM wrapper classes for encoded path segments (data contained in
|
||||
* instances of SVGPathData) is DOMSVGPathSeg and its sub-classes. Note that
|
||||
* there are multiple different DOM classes for path segs - one for each of the
|
||||
* 19 SVG 1.1 segment types.
|
||||
*/
|
||||
class SVGPathSegUtils
|
||||
{
|
||||
private:
|
||||
SVGPathSegUtils(){} // private to prevent instances
|
||||
|
||||
public:
|
||||
|
||||
static void GetValueAsString(const float *aSeg, nsAString& aValue);
|
||||
|
||||
/**
|
||||
* Encode a segment type enum to a float.
|
||||
*
|
||||
* At some point in the future we will likely want to encode other
|
||||
* information into the float, such as whether the command was explicit or
|
||||
* not. For now all this method does is save on int to float runtime
|
||||
* conversion by requiring PRUint32 and float to be of the same size so we
|
||||
* can simply do a bitwise PRUint32<->float copy.
|
||||
*/
|
||||
static float EncodeType(PRUint32 aType) {
|
||||
PR_STATIC_ASSERT(sizeof(PRUint32) == sizeof(float));
|
||||
NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
|
||||
return *(reinterpret_cast<float*>(&aType));
|
||||
}
|
||||
|
||||
static PRUint32 DecodeType(float aType) {
|
||||
PR_STATIC_ASSERT(sizeof(PRUint32) == sizeof(float));
|
||||
PRUint32 type = *(reinterpret_cast<PRUint32*>(&aType));
|
||||
NS_ABORT_IF_FALSE(IsValidType(type), "Seg type not recognized");
|
||||
return type;
|
||||
}
|
||||
|
||||
static PRUnichar GetPathSegTypeAsLetter(PRUint32 aType) {
|
||||
NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
|
||||
|
||||
static const PRUnichar table[] = {
|
||||
PRUnichar('x'), // 0 == PATHSEG_UNKNOWN
|
||||
PRUnichar('z'), // 1 == PATHSEG_CLOSEPATH
|
||||
PRUnichar('M'), // 2 == PATHSEG_MOVETO_ABS
|
||||
PRUnichar('m'), // 3 == PATHSEG_MOVETO_REL
|
||||
PRUnichar('L'), // 4 == PATHSEG_LINETO_ABS
|
||||
PRUnichar('l'), // 5 == PATHSEG_LINETO_REL
|
||||
PRUnichar('C'), // 6 == PATHSEG_CURVETO_CUBIC_ABS
|
||||
PRUnichar('c'), // 7 == PATHSEG_CURVETO_CUBIC_REL
|
||||
PRUnichar('Q'), // 8 == PATHSEG_CURVETO_QUADRATIC_ABS
|
||||
PRUnichar('q'), // 9 == PATHSEG_CURVETO_QUADRATIC_REL
|
||||
PRUnichar('A'), // 10 == PATHSEG_ARC_ABS
|
||||
PRUnichar('a'), // 11 == PATHSEG_ARC_REL
|
||||
PRUnichar('H'), // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
|
||||
PRUnichar('h'), // 13 == PATHSEG_LINETO_HORIZONTAL_REL
|
||||
PRUnichar('V'), // 14 == PATHSEG_LINETO_VERTICAL_ABS
|
||||
PRUnichar('v'), // 15 == PATHSEG_LINETO_VERTICAL_REL
|
||||
PRUnichar('S'), // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
|
||||
PRUnichar('s'), // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
|
||||
PRUnichar('T'), // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
|
||||
PRUnichar('t') // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
||||
};
|
||||
|
||||
return table[aType];
|
||||
}
|
||||
|
||||
static PRUint32 ArgCountForType(PRUint32 aType) {
|
||||
NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
|
||||
|
||||
static const PRUint8 table[] = {
|
||||
0, // 0 == PATHSEG_UNKNOWN
|
||||
0, // 1 == PATHSEG_CLOSEPATH
|
||||
2, // 2 == PATHSEG_MOVETO_ABS
|
||||
2, // 3 == PATHSEG_MOVETO_REL
|
||||
2, // 4 == PATHSEG_LINETO_ABS
|
||||
2, // 5 == PATHSEG_LINETO_REL
|
||||
6, // 6 == PATHSEG_CURVETO_CUBIC_ABS
|
||||
6, // 7 == PATHSEG_CURVETO_CUBIC_REL
|
||||
4, // 8 == PATHSEG_CURVETO_QUADRATIC_ABS
|
||||
4, // 9 == PATHSEG_CURVETO_QUADRATIC_REL
|
||||
7, // 10 == PATHSEG_ARC_ABS
|
||||
7, // 11 == PATHSEG_ARC_REL
|
||||
1, // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
|
||||
1, // 13 == PATHSEG_LINETO_HORIZONTAL_REL
|
||||
1, // 14 == PATHSEG_LINETO_VERTICAL_ABS
|
||||
1, // 15 == PATHSEG_LINETO_VERTICAL_REL
|
||||
4, // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
|
||||
4, // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
|
||||
2, // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
|
||||
2 // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
||||
};
|
||||
|
||||
return table[aType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience so that callers can pass a float containing an encoded type
|
||||
* and have it decoded implicitly.
|
||||
*/
|
||||
static PRUint32 ArgCountForType(float aType) {
|
||||
return ArgCountForType(DecodeType(aType));
|
||||
}
|
||||
|
||||
static inline PRBool IsValidType(PRUint32 aType) {
|
||||
return aType >= nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
|
||||
aType <= nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
|
||||
}
|
||||
|
||||
static inline 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) {
|
||||
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 void ToString(const float *aSeg, nsAString& aValue);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_SVGPATHSEGUTILS_H__
|
@ -46,7 +46,7 @@ load 436418-mpathRoot-1.svg
|
||||
load 448244-1.svg
|
||||
load 466576-1.xhtml
|
||||
load 499879-1.svg
|
||||
asserts(0-2) load 535691-1.svg # Bug 535691
|
||||
load 535691-1.svg
|
||||
load 539167-1.svg
|
||||
load 573316-1.svg
|
||||
load 579356-1.svg
|
||||
|
@ -74,6 +74,11 @@ public:
|
||||
nsIAtom **aLocalName) const;
|
||||
virtual nsSMILTargetAttrType GetTargetAttributeType() const;
|
||||
|
||||
// nsSVGElement
|
||||
virtual nsIAtom* GetPathDataAttrName() const {
|
||||
return nsGkAtoms::path;
|
||||
}
|
||||
|
||||
// Utility method to let our <mpath> children tell us when they've changed,
|
||||
// so we can make sure our mAnimationFunction is marked as having changed.
|
||||
void MpathChanged() { mAnimationFunction.MpathChanged(); }
|
||||
|
@ -458,6 +458,8 @@ nsSVGAnimationElement::BeginElement(void)
|
||||
NS_IMETHODIMP
|
||||
nsSVGAnimationElement::BeginElementAt(float offset)
|
||||
{
|
||||
NS_ENSURE_FINITE(offset, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// This will fail if we're not attached to a time container (SVG document
|
||||
// fragment).
|
||||
nsresult rv = mTimedElement.BeginElementAt(offset);
|
||||
@ -480,6 +482,8 @@ nsSVGAnimationElement::EndElement(void)
|
||||
NS_IMETHODIMP
|
||||
nsSVGAnimationElement::EndElementAt(float offset)
|
||||
{
|
||||
NS_ENSURE_FINITE(offset, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
nsresult rv = mTimedElement.EndElementAt(offset);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
@ -75,6 +75,7 @@
|
||||
#include "nsSVGViewBox.h"
|
||||
#include "nsSVGString.h"
|
||||
#include "SVGAnimatedLengthList.h"
|
||||
#include "SVGAnimatedPathSegList.h"
|
||||
#include "nsIDOMSVGUnitTypes.h"
|
||||
#include "nsIDOMSVGNumberList.h"
|
||||
#include "nsIDOMSVGAnimatedNumberList.h"
|
||||
@ -179,6 +180,9 @@ nsSVGElement::Init()
|
||||
lengthListInfo.Reset(i);
|
||||
}
|
||||
|
||||
// No need to reset SVGPathData since the default value in always the same
|
||||
// (an empty list).
|
||||
|
||||
StringAttributesInfo stringInfo = GetStringInfo();
|
||||
|
||||
for (i = 0; i < stringInfo.mStringCount; i++) {
|
||||
@ -373,6 +377,22 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMatch) {
|
||||
// Check for SVGAnimatedPathSegList attribute
|
||||
if (GetPathDataAttrName() == aAttribute) {
|
||||
SVGAnimatedPathSegList* segList = GetAnimPathSegList();
|
||||
if (segList) {
|
||||
rv = segList->SetBaseValueString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
|
||||
// The spec says we parse everything up to the failure, so we don't
|
||||
// call segList->ClearBaseValue()
|
||||
}
|
||||
foundMatch = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMatch) {
|
||||
// Check for nsSVGNumber2 attribute
|
||||
NumberAttributesInfo numberInfo = GetNumberInfo();
|
||||
@ -568,6 +588,18 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMatch) {
|
||||
// Check if this is a path segment list attribute going away
|
||||
if (GetPathDataAttrName() == aName) {
|
||||
SVGAnimatedPathSegList *segList = GetAnimPathSegList();
|
||||
if (segList) {
|
||||
segList->ClearBaseValue();
|
||||
DidChangePathSegList(PR_FALSE);
|
||||
foundMatch = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMatch) {
|
||||
// Check if this is a number attribute going away
|
||||
NumberAttributesInfo numInfo = GetNumberInfo();
|
||||
@ -1546,6 +1578,36 @@ nsSVGElement::GetAnimatedLengthList(PRUint8 aAttrEnum)
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsSVGElement::DidChangePathSegList(PRBool aDoSetAttr)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(GetPathDataAttrName(), "Changing non-existant path data?");
|
||||
|
||||
if (!aDoSetAttr)
|
||||
return;
|
||||
|
||||
nsAutoString newStr;
|
||||
GetAnimPathSegList()->GetBaseValue().GetValueAsString(newStr);
|
||||
|
||||
SetAttr(kNameSpaceID_None, GetPathDataAttrName(), newStr, PR_TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGElement::DidAnimatePathSegList()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(GetPathDataAttrName(),
|
||||
"Animatinging non-existant path data?");
|
||||
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
|
||||
if (frame) {
|
||||
frame->AttributeChanged(kNameSpaceID_None,
|
||||
GetPathDataAttrName(),
|
||||
nsIDOMMutationEvent::MODIFICATION);
|
||||
}
|
||||
}
|
||||
|
||||
nsSVGElement::NumberAttributesInfo
|
||||
nsSVGElement::GetNumberInfo()
|
||||
{
|
||||
@ -2215,6 +2277,16 @@ nsSVGElement::GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName)
|
||||
}
|
||||
}
|
||||
|
||||
// PathSegLists:
|
||||
{
|
||||
if (GetPathDataAttrName() == aName) {
|
||||
SVGAnimatedPathSegList *segList = GetAnimPathSegList();
|
||||
if (segList) {
|
||||
return segList->ToSMILAttr(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mapped attributes:
|
||||
if (IsAttributeMapped(aName)) {
|
||||
nsCSSProperty prop =
|
||||
|
@ -74,6 +74,7 @@ struct gfxMatrix;
|
||||
namespace mozilla {
|
||||
class SVGAnimatedLengthList;
|
||||
class SVGUserUnitList;
|
||||
class SVGAnimatedPathSegList;
|
||||
}
|
||||
|
||||
typedef nsStyledElement nsSVGElementBase;
|
||||
@ -87,6 +88,10 @@ protected:
|
||||
virtual ~nsSVGElement();
|
||||
|
||||
public:
|
||||
typedef mozilla::SVGUserUnitList SVGUserUnitList;
|
||||
typedef mozilla::SVGAnimatedLengthList SVGAnimatedLengthList;
|
||||
typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList;
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
@ -167,6 +172,7 @@ public:
|
||||
virtual void DidChangeViewBox(PRBool aDoSetAttr);
|
||||
virtual void DidChangePreserveAspectRatio(PRBool aDoSetAttr);
|
||||
virtual void DidChangeLengthList(PRUint8 aAttrEnum, PRBool aDoSetAttr);
|
||||
virtual void DidChangePathSegList(PRBool aDoSetAttr);
|
||||
virtual void DidChangeString(PRUint8 aAttrEnum) {}
|
||||
|
||||
virtual void DidAnimateLength(PRUint8 aAttrEnum);
|
||||
@ -178,19 +184,30 @@ public:
|
||||
virtual void DidAnimateViewBox();
|
||||
virtual void DidAnimatePreserveAspectRatio();
|
||||
virtual void DidAnimateLengthList(PRUint8 aAttrEnum);
|
||||
virtual void DidAnimatePathSegList();
|
||||
virtual void DidAnimateTransform();
|
||||
virtual void DidAnimateString(PRUint8 aAttrEnum);
|
||||
|
||||
void GetAnimatedLengthValues(float *aFirst, ...);
|
||||
void GetAnimatedNumberValues(float *aFirst, ...);
|
||||
void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
|
||||
void GetAnimatedLengthListValues(mozilla::SVGUserUnitList *aFirst, ...);
|
||||
mozilla::SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
|
||||
void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
|
||||
SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
|
||||
virtual SVGAnimatedPathSegList* GetAnimPathSegList() {
|
||||
// DOM interface 'SVGAnimatedPathData' (*inherited* by nsSVGPathElement)
|
||||
// has a member called 'animatedPathSegList' member, so we have a shorter
|
||||
// name so we don't get hidden by the GetAnimatedPathSegList declared by
|
||||
// NS_DECL_NSIDOMSVGANIMATEDPATHDATA.
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
virtual nsISMILAttr* GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName);
|
||||
void AnimationNeedsResample();
|
||||
void FlushAnimations();
|
||||
#else
|
||||
void AnimationNeedsResample() { /* do nothing */ }
|
||||
void FlushAnimations() { /* do nothing */ }
|
||||
#endif
|
||||
|
||||
virtual void RecompileScriptEventListeners();
|
||||
@ -198,6 +215,10 @@ public:
|
||||
void GetStringBaseValue(PRUint8 aAttrEnum, nsAString& aResult) const;
|
||||
void SetStringBaseValue(PRUint8 aAttrEnum, const nsAString& aValue);
|
||||
|
||||
virtual nsIAtom* GetPathDataAttrName() const {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
const nsAString* aValue, PRBool aNotify);
|
||||
@ -357,11 +378,11 @@ protected:
|
||||
};
|
||||
|
||||
struct LengthListAttributesInfo {
|
||||
mozilla::SVGAnimatedLengthList* mLengthLists;
|
||||
SVGAnimatedLengthList* mLengthLists;
|
||||
LengthListInfo* mLengthListInfo;
|
||||
PRUint32 mLengthListCount;
|
||||
|
||||
LengthListAttributesInfo(mozilla::SVGAnimatedLengthList *aLengthLists,
|
||||
LengthListAttributesInfo(SVGAnimatedLengthList *aLengthLists,
|
||||
LengthListInfo *aLengthListInfo,
|
||||
PRUint32 aLengthListCount)
|
||||
: mLengthLists(aLengthLists)
|
||||
|
@ -38,13 +38,14 @@
|
||||
|
||||
#include "nsSVGPathDataParser.h"
|
||||
#include "nsSVGDataParser.h"
|
||||
#include "nsSVGPathSeg.h"
|
||||
#include "nsSVGPathElement.h"
|
||||
#include "prdtoa.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsresult nsSVGPathDataParser::Match()
|
||||
{
|
||||
return MatchSvgPath();
|
||||
@ -866,195 +867,7 @@ PRBool nsSVGPathDataParser::IsTokenEllipticalArcArgStarter()
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// nsSVGPathDataParserToInternal
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::Parse(const nsAString &aValue)
|
||||
{
|
||||
mPathData->Clear();
|
||||
mPx = mPy = mCx = mCy = mStartX = mStartY = 0;
|
||||
mNumCommands = mNumArguments = 0;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
|
||||
nsresult rv = nsSVGPathDataParser::Parse(aValue);
|
||||
|
||||
PathFini();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreMoveTo(PRBool absCoords, float x, float y)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
y += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL;
|
||||
}
|
||||
return PathMoveTo(x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreClosePath()
|
||||
{
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH;
|
||||
|
||||
return PathClose();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreLineTo(PRBool absCoords, float x, float y)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
y += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_REL;
|
||||
}
|
||||
return PathLineTo(x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreHLineTo(PRBool absCoords, float x)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL;
|
||||
}
|
||||
return PathLineTo(x, mPy);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreVLineTo(PRBool absCoords, float y)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS;
|
||||
} else {
|
||||
y += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL;
|
||||
}
|
||||
return PathLineTo(mPx, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS;
|
||||
} else {
|
||||
x += mPx; x1 += mPx; x2 += mPx;
|
||||
y += mPy; y1 += mPy; y2 += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL;
|
||||
}
|
||||
mCx = x2;
|
||||
mCy = y2;
|
||||
return PathCurveTo(x1, y1, x2, y2, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreSmoothCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x2, float y2)
|
||||
{
|
||||
float x1, y1;
|
||||
|
||||
// first controlpoint = reflection last one about current point
|
||||
if (mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
|
||||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
|
||||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
|
||||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
|
||||
x1 = 2 * mPx - mCx;
|
||||
y1 = 2 * mPy - mCy;
|
||||
} else {
|
||||
x1 = mPx;
|
||||
y1 = mPy;
|
||||
}
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
x2 += mPx;
|
||||
y += mPy;
|
||||
y2 += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
|
||||
}
|
||||
mCx = x2;
|
||||
mCy = y2;
|
||||
return PathCurveTo(x1, y1, x2, y2, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreQuadCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x1, float y1)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
x1 += mPx;
|
||||
y += mPy;
|
||||
y1 += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL;
|
||||
}
|
||||
|
||||
float x31, y31, x32, y32;
|
||||
// conversion of quadratic bezier curve to cubic bezier curve:
|
||||
x31 = mPx + (x1 - mPx) * 2 / 3;
|
||||
y31 = mPy + (y1 - mPy) * 2 / 3;
|
||||
x32 = x1 + (x - x1) / 3;
|
||||
y32 = y1 + (y - y1) / 3;
|
||||
|
||||
mCx = x1;
|
||||
mCy = y1;
|
||||
return PathCurveTo(x31, y31, x32, y32, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(PRBool absCoords,
|
||||
float x, float y)
|
||||
{
|
||||
float x1, y1;
|
||||
|
||||
// first controlpoint = reflection last one about current point
|
||||
if (mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
|
||||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
|
||||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
|
||||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
|
||||
x1 = 2 * mPx - mCx;
|
||||
y1 = 2 * mPy - mCy;
|
||||
} else {
|
||||
x1 = mPx;
|
||||
y1 = mPy;
|
||||
}
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
y += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
|
||||
}
|
||||
|
||||
float x31, y31, x32, y32;
|
||||
// conversion of quadratic bezier curve to cubic bezier curve:
|
||||
x31 = mPx + (x1 - mPx) * 2 / 3;
|
||||
y31 = mPy + (y1 - mPy) * 2 / 3;
|
||||
x32 = x1 + (x - x1) / 3;
|
||||
y32 = y1 + (y - y1) / 3;
|
||||
|
||||
mCx = x1;
|
||||
mCy = y1;
|
||||
return PathCurveTo(x31, y31, x32, y32, x, y);
|
||||
}
|
||||
|
||||
static double
|
||||
CalcVectorAngle(double ux, double uy, double vx, double vy)
|
||||
@ -1066,300 +879,26 @@ CalcVectorAngle(double ux, double uy, double vx, double vy)
|
||||
return 2 * M_PI - (ta-tb);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::ConvertArcToCurves(float x2, float y2,
|
||||
float rx, float ry,
|
||||
float angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag)
|
||||
{
|
||||
float x1=mPx, y1=mPy, x3, y3;
|
||||
// Treat out-of-range parameters as described in
|
||||
// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
|
||||
// If the endpoints (x1, y1) and (x2, y2) are identical, then this
|
||||
// is equivalent to omitting the elliptical arc segment entirely
|
||||
if (x1 == x2 && y1 == y2) {
|
||||
return NS_OK;
|
||||
}
|
||||
// If rX = 0 or rY = 0 then this arc is treated as a straight line
|
||||
// segment (a "lineto") joining the endpoints.
|
||||
if (rx == 0.0f || ry == 0.0f) {
|
||||
return PathLineTo(x2, y2);
|
||||
}
|
||||
nsSVGArcConverter converter(x1, y1, x2, y2, rx, ry, angle,
|
||||
largeArcFlag, sweepFlag);
|
||||
|
||||
while (converter.GetNextSegment(&x1, &y1, &x2, &y2, &x3, &y3)) {
|
||||
// c) draw the cubic bezier:
|
||||
nsresult rv = PathCurveTo(x1, y1, x2, y2, x3, y3);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreEllipticalArc(PRBool absCoords,
|
||||
float x, float y,
|
||||
float r1, float r2,
|
||||
float angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag)
|
||||
{
|
||||
if (absCoords) {
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_ARC_ABS;
|
||||
} else {
|
||||
x += mPx;
|
||||
y += mPy;
|
||||
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_ARC_REL;
|
||||
}
|
||||
return ConvertArcToCurves(x, y, r1, r2, angle, largeArcFlag, sweepFlag);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::PathEnsureSpace(PRUint32 aNumArgs)
|
||||
{
|
||||
if (!(mNumCommands % 4) &&
|
||||
!mCommands.AppendElement())
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
if (!mArguments.SetLength(mArguments.Length()+aNumArgs))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathDataParserToInternal::PathAddCommandCode(PRUint8 aCommand)
|
||||
{
|
||||
PRUint32 offset = mNumCommands / 4;
|
||||
PRUint32 shift = 2 * (mNumCommands % 4);
|
||||
if (shift == 0) {
|
||||
// make sure we set the byte, to avoid false UMR reports
|
||||
mCommands[offset] = aCommand;
|
||||
} else {
|
||||
mCommands[offset] |= aCommand << shift;
|
||||
}
|
||||
mNumCommands++;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::PathMoveTo(float x, float y)
|
||||
{
|
||||
nsresult rv = PathEnsureSpace(2);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PathAddCommandCode(nsSVGPathList::MOVETO);
|
||||
mArguments[mNumArguments++] = x;
|
||||
mArguments[mNumArguments++] = y;
|
||||
|
||||
mPx = mStartX = x;
|
||||
mPy = mStartY = y;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::PathLineTo(float x, float y)
|
||||
{
|
||||
nsresult rv = PathEnsureSpace(2);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PathAddCommandCode(nsSVGPathList::LINETO);
|
||||
mArguments[mNumArguments++] = x;
|
||||
mArguments[mNumArguments++] = y;
|
||||
|
||||
mPx = x;
|
||||
mPy = y;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::PathCurveTo(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3)
|
||||
{
|
||||
nsresult rv = PathEnsureSpace(6);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PathAddCommandCode(nsSVGPathList::CURVETO);
|
||||
mArguments[mNumArguments++] = x1;
|
||||
mArguments[mNumArguments++] = y1;
|
||||
mArguments[mNumArguments++] = x2;
|
||||
mArguments[mNumArguments++] = y2;
|
||||
mArguments[mNumArguments++] = x3;
|
||||
mArguments[mNumArguments++] = y3;
|
||||
|
||||
mPx = x3;
|
||||
mPy = y3;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::PathClose()
|
||||
{
|
||||
nsresult rv = PathEnsureSpace(0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PathAddCommandCode(nsSVGPathList::CLOSEPATH);
|
||||
|
||||
mPx = mStartX;
|
||||
mPy = mStartY;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathDataParserToInternal::PathFini()
|
||||
{
|
||||
// We're done adding data to the arrays - copy to a straight array
|
||||
// in mPathData, which allows us to remove the 8-byte overhead per
|
||||
// nsTArray. For a bonus savings we allocate a single array instead
|
||||
// of two.
|
||||
PRUint32 argArraySize;
|
||||
|
||||
argArraySize = mArguments.Length() * sizeof(float);
|
||||
mPathData->mArguments = (float *)malloc(argArraySize + mCommands.Length());
|
||||
if (!mPathData->mArguments)
|
||||
return;
|
||||
|
||||
memcpy(mPathData->mArguments, mArguments.Elements(), argArraySize);
|
||||
memcpy(mPathData->mArguments + mNumArguments,
|
||||
mCommands.Elements(),
|
||||
mCommands.Length());
|
||||
mPathData->mNumArguments = mNumArguments;
|
||||
mPathData->mNumCommands = mNumCommands;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// nsSVGPathDataParserToDOM
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::AppendSegment(nsIDOMSVGPathSeg* seg)
|
||||
{
|
||||
NS_ENSURE_TRUE(seg, NS_ERROR_OUT_OF_MEMORY);
|
||||
mData->AppendObject(seg);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreMoveTo(PRBool absCoords, float x, float y)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegMovetoAbs(x, y)
|
||||
: NS_NewSVGPathSegMovetoRel(x, y));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreClosePath()
|
||||
{
|
||||
return AppendSegment(NS_NewSVGPathSegClosePath());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreLineTo(PRBool absCoords, float x, float y)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegLinetoAbs(x, y)
|
||||
: NS_NewSVGPathSegLinetoRel(x, y));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreHLineTo(PRBool absCoords, float x)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegLinetoHorizontalAbs(x)
|
||||
: NS_NewSVGPathSegLinetoHorizontalRel(x));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreVLineTo(PRBool absCoords, float y)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegLinetoVerticalAbs(y)
|
||||
: NS_NewSVGPathSegLinetoVerticalRel(y));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2)
|
||||
: NS_NewSVGPathSegCurvetoCubicRel(x, y, x1, y1, x2, y2));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreSmoothCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x2, float y2)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2)
|
||||
: NS_NewSVGPathSegCurvetoCubicSmoothRel(x, y, x2, y2));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreQuadCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x1, float y1)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1)
|
||||
: NS_NewSVGPathSegCurvetoQuadraticRel(x, y, x1, y1));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreSmoothQuadCurveTo(PRBool absCoords,
|
||||
float x, float y)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(x, y)
|
||||
: NS_NewSVGPathSegCurvetoQuadraticSmoothRel(x, y));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToDOM::StoreEllipticalArc(PRBool absCoords,
|
||||
float x, float y,
|
||||
float r1, float r2,
|
||||
float angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag)
|
||||
{
|
||||
return AppendSegment(
|
||||
absCoords ? NS_NewSVGPathSegArcAbs(x, y, r1, r2, angle,
|
||||
largeArcFlag, sweepFlag)
|
||||
: NS_NewSVGPathSegArcRel(x, y, r1, r2, angle,
|
||||
largeArcFlag, sweepFlag));
|
||||
}
|
||||
|
||||
nsSVGArcConverter::nsSVGArcConverter(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float rx, float ry,
|
||||
float angle,
|
||||
nsSVGArcConverter::nsSVGArcConverter(const gfxPoint &from,
|
||||
const gfxPoint &to,
|
||||
const gfxPoint &radii,
|
||||
double angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag)
|
||||
{
|
||||
const double radPerDeg = M_PI/180.0;
|
||||
|
||||
// If rX or rY have negative signs, these are dropped; the absolute
|
||||
// value is used instead.
|
||||
mRx = fabs(rx);
|
||||
mRy = fabs(ry);
|
||||
|
||||
// Convert to center parameterization as shown in
|
||||
// http://www.w3.org/TR/SVG/implnote.html
|
||||
mRx = fabs(radii.x);
|
||||
mRy = fabs(radii.y);
|
||||
|
||||
mSinPhi = sin(angle*radPerDeg);
|
||||
mCosPhi = cos(angle*radPerDeg);
|
||||
|
||||
double x1dash = mCosPhi * (x1-x2)/2.0 + mSinPhi * (y1-y2)/2.0;
|
||||
double y1dash = -mSinPhi * (x1-x2)/2.0 + mCosPhi * (y1-y2)/2.0;
|
||||
double x1dash = mCosPhi * (from.x-to.x)/2.0 + mSinPhi * (from.y-to.y)/2.0;
|
||||
double y1dash = -mSinPhi * (from.x-to.x)/2.0 + mCosPhi * (from.y-to.y)/2.0;
|
||||
|
||||
double root;
|
||||
double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash -
|
||||
@ -1367,13 +906,13 @@ nsSVGArcConverter::nsSVGArcConverter(float x1, float y1,
|
||||
|
||||
if (numerator < 0.0) {
|
||||
// If mRx , mRy and are such that there is no solution (basically,
|
||||
// the ellipse is not big enough to reach from (x1, y1) to (x2,
|
||||
// y2)) then the ellipse is scaled up uniformly until there is
|
||||
// the ellipse is not big enough to reach from 'from' to 'to'
|
||||
// then the ellipse is scaled up uniformly until there is
|
||||
// exactly one solution (until the ellipse is just big enough).
|
||||
|
||||
// -> find factor s, such that numerator' with mRx'=s*mRx and
|
||||
// mRy'=s*mRy becomes 0 :
|
||||
float s = (float)sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
|
||||
double s = sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
|
||||
|
||||
mRx *= s;
|
||||
mRy *= s;
|
||||
@ -1388,8 +927,8 @@ nsSVGArcConverter::nsSVGArcConverter(float x1, float y1,
|
||||
double cxdash = root*mRx*y1dash/mRy;
|
||||
double cydash = -root*mRy*x1dash/mRx;
|
||||
|
||||
mCx = mCosPhi * cxdash - mSinPhi * cydash + (x1+x2)/2.0;
|
||||
mCy = mSinPhi * cxdash + mCosPhi * cydash + (y1+y2)/2.0;
|
||||
mC.x = mCosPhi * cxdash - mSinPhi * cydash + (from.x+to.x)/2.0;
|
||||
mC.y = mSinPhi * cxdash + mCosPhi * cydash + (from.y+to.y)/2.0;
|
||||
mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy);
|
||||
double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy,
|
||||
(-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy);
|
||||
@ -1403,43 +942,179 @@ nsSVGArcConverter::nsSVGArcConverter(float x1, float y1,
|
||||
mDelta = dtheta/mNumSegs;
|
||||
mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0);
|
||||
|
||||
mX1 = x1;
|
||||
mY1 = y1;
|
||||
mFrom = from;
|
||||
mSegIndex = 0;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSVGArcConverter::GetNextSegment(float *x1, float *y1,
|
||||
float *x2, float *y2,
|
||||
float *x3, float *y3)
|
||||
nsSVGArcConverter::GetNextSegment(gfxPoint *cp1, gfxPoint *cp2, gfxPoint *to)
|
||||
{
|
||||
if (mSegIndex == mNumSegs) {
|
||||
return PR_FALSE;
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
float cosTheta1 = cos(mTheta);
|
||||
float sinTheta1 = sin(mTheta);
|
||||
float theta2 = mTheta + mDelta;
|
||||
float cosTheta2 = cos(theta2);
|
||||
float sinTheta2 = sin(theta2);
|
||||
double cosTheta1 = cos(mTheta);
|
||||
double sinTheta1 = sin(mTheta);
|
||||
double theta2 = mTheta + mDelta;
|
||||
double cosTheta2 = cos(theta2);
|
||||
double sinTheta2 = sin(theta2);
|
||||
|
||||
// a) calculate endpoint of the segment:
|
||||
*x3 = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mCx;
|
||||
*y3 = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mCy;
|
||||
to->x = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mC.x;
|
||||
to->y = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mC.y;
|
||||
|
||||
// b) calculate gradients at start/end points of segment:
|
||||
*x1 = mX1 + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
|
||||
*y1 = mY1 + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
|
||||
cp1->x = mFrom.x + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
|
||||
cp1->y = mFrom.y + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
|
||||
|
||||
*x2 = *x3 + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
|
||||
*y2 = *y3 + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
|
||||
cp2->x = to->x + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
|
||||
cp2->y = to->y + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
|
||||
|
||||
// do next segment
|
||||
mTheta = theta2;
|
||||
mX1 = *x3;
|
||||
mY1 = *y3;
|
||||
mFrom = *to;
|
||||
++mSegIndex;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// nsSVGPathDataParserToInternal
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::Parse(const nsAString &aValue)
|
||||
{
|
||||
mPathSegList->Clear();
|
||||
return nsSVGPathDataParser::Parse(aValue);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreMoveTo(PRBool absCoords, float x, float y)
|
||||
{
|
||||
// Because our IDL compiler doesn't know any better, each seg type constant
|
||||
// in nsIDOMSVGPathSeg is in a separate enum. This results in "warning:
|
||||
// enumeral mismatch in conditional expression" under GCC if two bare
|
||||
// nsIDOMSVGPathSeg constants are used as operands of the ?: operator below.
|
||||
// In newer versions of GCC we would be able to turn off this warning using:
|
||||
//
|
||||
//#pragma GCC diagnostic push
|
||||
//#pragma GCC diagnostic ignored "-Wenum-compare"
|
||||
//...
|
||||
//#pragma GCC diagnostic pop
|
||||
//
|
||||
// Unfortunately we need to support older versions of GCC. Instead, to
|
||||
// eliminate this warning noise being sent to the console, we wrap the
|
||||
// operands with PRUint32(...).
|
||||
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreClosePath()
|
||||
{
|
||||
return mPathSegList->AppendSeg(nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreLineTo(PRBool absCoords, float x, float y)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreHLineTo(PRBool absCoords, float x)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreVLineTo(PRBool absCoords, float y)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x1, y1, x2, y2, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreSmoothCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x2, float y2)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x2, y2, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreQuadCurveTo(PRBool absCoords,
|
||||
float x, float y,
|
||||
float x1, float y1)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x1, y1, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(PRBool absCoords,
|
||||
float x, float y)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
|
||||
|
||||
return mPathSegList->AppendSeg(type, x, y);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathDataParserToInternal::StoreEllipticalArc(PRBool absCoords,
|
||||
float x, float y,
|
||||
float r1, float r2,
|
||||
float angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag)
|
||||
{
|
||||
PRUint32 type = absCoords ?
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) :
|
||||
PRUint32(nsIDOMSVGPathSeg::PATHSEG_ARC_REL);
|
||||
|
||||
// We can only pass floats after 'type', and per the SVG spec for arc,
|
||||
// non-zero args are treated at 'true'.
|
||||
return mPathSegList->AppendSeg(type, r1, r2, angle,
|
||||
largeArcFlag ? 1.0f : 0.0f,
|
||||
sweepFlag ? 1.0f : 0.0f,
|
||||
x, y);
|
||||
}
|
||||
|
||||
|
@ -44,9 +44,14 @@
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIDOMSVGPathSeg.h"
|
||||
#include "nsTArray.h"
|
||||
#include "gfxPoint.h"
|
||||
|
||||
class nsSVGPathList;
|
||||
|
||||
namespace mozilla {
|
||||
class SVGPathData;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsSVGPathDataParser: a simple recursive descent parser that builds
|
||||
// nsIDOMSVGPathSegs from path data strings. The grammar for path data
|
||||
@ -140,10 +145,30 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
class nsSVGArcConverter
|
||||
{
|
||||
public:
|
||||
nsSVGArcConverter(const gfxPoint &from,
|
||||
const gfxPoint &to,
|
||||
const gfxPoint &radii,
|
||||
double angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag);
|
||||
PRBool GetNextSegment(gfxPoint *cp1, gfxPoint *cp2, gfxPoint *to);
|
||||
protected:
|
||||
PRInt32 mNumSegs, mSegIndex;
|
||||
double mTheta, mDelta, mT;
|
||||
double mSinPhi, mCosPhi;
|
||||
double mRx, mRy;
|
||||
gfxPoint mFrom, mC;
|
||||
};
|
||||
|
||||
class nsSVGPathDataParserToInternal : public nsSVGPathDataParser
|
||||
{
|
||||
public:
|
||||
nsSVGPathDataParserToInternal(nsSVGPathList *data) : mPathData(data) {}
|
||||
nsSVGPathDataParserToInternal(mozilla::SVGPathData *aList)
|
||||
: mPathSegList(aList)
|
||||
{}
|
||||
nsresult Parse(const nsAString &aValue);
|
||||
|
||||
protected:
|
||||
@ -165,78 +190,7 @@ protected:
|
||||
PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
private:
|
||||
nsSVGPathList *mPathData;
|
||||
PRUint16 mPrevSeg; // previous segment type for "smooth" segments"
|
||||
float mPx, mPy; // current point
|
||||
float mCx, mCy; // last control point for "smooth" segments
|
||||
float mStartX, mStartY; // start of current subpath, for closepath
|
||||
|
||||
// information used to construct PathList
|
||||
nsTArray<PRUint8> mCommands;
|
||||
nsTArray<float> mArguments;
|
||||
PRUint32 mNumArguments;
|
||||
PRUint32 mNumCommands;
|
||||
|
||||
// Pathdata helpers
|
||||
nsresult ConvertArcToCurves(float x2, float y2, float rx, float ry,
|
||||
float angle, PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
nsresult PathEnsureSpace(PRUint32 aNumArgs);
|
||||
void PathAddCommandCode(PRUint8 aCommand);
|
||||
nsresult PathMoveTo(float x, float y);
|
||||
nsresult PathLineTo(float x, float y);
|
||||
nsresult PathCurveTo(float x1, float y1, float x2, float y2, float x3, float y3);
|
||||
nsresult PathClose();
|
||||
void PathFini();
|
||||
};
|
||||
|
||||
class nsSVGPathDataParserToDOM : public nsSVGPathDataParser
|
||||
{
|
||||
public:
|
||||
nsSVGPathDataParserToDOM(nsCOMArray<nsIDOMSVGPathSeg>* data) : mData(data) {}
|
||||
|
||||
protected:
|
||||
virtual nsresult StoreMoveTo(PRBool absCoords, float x, float y);
|
||||
virtual nsresult StoreClosePath();
|
||||
virtual nsresult StoreLineTo(PRBool absCoords, float x, float y);
|
||||
virtual nsresult StoreHLineTo(PRBool absCoords, float x);
|
||||
virtual nsresult StoreVLineTo(PRBool absCoords, float y);
|
||||
virtual nsresult StoreCurveTo(PRBool absCoords, float x, float y,
|
||||
float x1, float y1, float x2, float y2);
|
||||
virtual nsresult StoreSmoothCurveTo(PRBool absCoords, float x, float y,
|
||||
float x2, float y2);
|
||||
virtual nsresult StoreQuadCurveTo(PRBool absCoords, float x, float y,
|
||||
float x1, float y1);
|
||||
virtual nsresult StoreSmoothQuadCurveTo(PRBool absCoords,
|
||||
float x, float y);
|
||||
virtual nsresult StoreEllipticalArc(PRBool absCoords, float x, float y,
|
||||
float r1, float r2, float angle,
|
||||
PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
private:
|
||||
nsresult AppendSegment(nsIDOMSVGPathSeg* seg);
|
||||
|
||||
nsCOMArray<nsIDOMSVGPathSeg>* mData;
|
||||
};
|
||||
|
||||
class nsSVGArcConverter
|
||||
{
|
||||
public:
|
||||
nsSVGArcConverter(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float rx, float ry,
|
||||
float angle,
|
||||
PRBool largeArcFlag,
|
||||
PRBool sweepFlag);
|
||||
PRBool GetNextSegment(float *x1, float *y1,
|
||||
float *x2, float *y2,
|
||||
float *x3, float *y3);
|
||||
protected:
|
||||
PRInt32 mNumSegs, mSegIndex;
|
||||
float mTheta, mDelta, mT;
|
||||
float mSinPhi, mCosPhi;
|
||||
float mX1, mY1, mRx, mRy, mCx, mCy;
|
||||
|
||||
mozilla::SVGPathData *mPathSegList;
|
||||
};
|
||||
|
||||
#endif // __NS_SVGPATHDATAPARSER_H__
|
||||
|
@ -37,9 +37,9 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsSVGPathSegList.h"
|
||||
#include "nsIDOMSVGPathSeg.h"
|
||||
#include "nsSVGPathSeg.h"
|
||||
#include "DOMSVGPathSeg.h"
|
||||
#include "DOMSVGPathSegList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsSVGPathDataParser.h"
|
||||
@ -50,6 +50,8 @@
|
||||
#include "gfxContext.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsSVGElement::NumberInfo nsSVGPathElement::sNumberInfo =
|
||||
{ &nsGkAtoms::pathLength, 0 };
|
||||
|
||||
@ -78,12 +80,6 @@ nsSVGPathElement::nsSVGPathElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
{
|
||||
}
|
||||
|
||||
nsSVGPathElement::~nsSVGPathElement()
|
||||
{
|
||||
if (mSegments)
|
||||
NS_REMOVE_SVGVALUE_OBSERVER(mSegments);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMNode methods
|
||||
|
||||
@ -141,38 +137,7 @@ NS_IMETHODIMP
|
||||
nsSVGPathElement::GetPathSegAtLength(float distance, PRUint32 *_retval)
|
||||
{
|
||||
NS_ENSURE_FINITE(distance, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
//Check if mSegments is null
|
||||
nsresult rv = CreatePathSegList();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 i = 0, numSegments;
|
||||
float distCovered = 0;
|
||||
nsSVGPathSegTraversalState ts;
|
||||
|
||||
mSegments->GetNumberOfItems(&numSegments);
|
||||
|
||||
// There is no need to check to see if distance falls within the last segment
|
||||
// because if distance is longer than the total length of the path we return
|
||||
// the index of the final segment anyway.
|
||||
while (distCovered < distance && i + 1 < numSegments) {
|
||||
nsCOMPtr<nsIDOMSVGPathSeg> segment;
|
||||
mSegments->GetItem(i, getter_AddRefs(segment));
|
||||
nsSVGPathSeg* curSeg = static_cast<nsSVGPathSeg*>(segment.get());
|
||||
if (i == 0) {
|
||||
curSeg->GetLength(&ts);
|
||||
} else {
|
||||
distCovered += curSeg->GetLength(&ts);
|
||||
}
|
||||
|
||||
if (distCovered >= distance) {
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
*_retval = i;
|
||||
|
||||
*_retval = mD.GetAnimValue().GetPathSegAtLength(distance);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -367,26 +332,6 @@ nsSVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, ns
|
||||
return CallQueryInterface(seg, _retval);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathElement::CreatePathSegList()
|
||||
{
|
||||
if (mSegments)
|
||||
return NS_OK;
|
||||
|
||||
nsresult rv = NS_NewSVGPathSegList(getter_AddRefs(mSegments));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
|
||||
|
||||
nsAutoString d;
|
||||
if (NS_SUCCEEDED(GetAttr(kNameSpaceID_None, nsGkAtoms::d, d)))
|
||||
value->SetValueString(d);
|
||||
|
||||
NS_ADD_SVGVALUE_OBSERVER(mSegments);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
@ -402,12 +347,9 @@ nsSVGPathElement::GetNumberInfo()
|
||||
/* readonly attribute nsIDOMSVGPathSegList pathSegList; */
|
||||
NS_IMETHODIMP nsSVGPathElement::GetPathSegList(nsIDOMSVGPathSegList * *aPathSegList)
|
||||
{
|
||||
nsresult rv = CreatePathSegList();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aPathSegList = mSegments;
|
||||
NS_ADDREF(*aPathSegList);
|
||||
return NS_OK;
|
||||
void *key = mD.GetBaseValKey();
|
||||
*aPathSegList = DOMSVGPathSegList::GetDOMWrapper(key, this, PR_FALSE).get();
|
||||
return *aPathSegList ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* readonly attribute nsIDOMSVGPathSegList normalizedPathSegList; */
|
||||
@ -419,12 +361,10 @@ NS_IMETHODIMP nsSVGPathElement::GetNormalizedPathSegList(nsIDOMSVGPathSegList *
|
||||
/* readonly attribute nsIDOMSVGPathSegList animatedPathSegList; */
|
||||
NS_IMETHODIMP nsSVGPathElement::GetAnimatedPathSegList(nsIDOMSVGPathSegList * *aAnimatedPathSegList)
|
||||
{
|
||||
nsresult rv = CreatePathSegList();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aAnimatedPathSegList = mSegments;
|
||||
NS_ADDREF(*aAnimatedPathSegList);
|
||||
return NS_OK;
|
||||
void *key = mD.GetAnimValKey();
|
||||
*aAnimatedPathSegList =
|
||||
DOMSVGPathSegList::GetDOMWrapper(key, this, PR_TRUE).get();
|
||||
return *aAnimatedPathSegList ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* readonly attribute nsIDOMSVGPathSegList animatedNormalizedPathSegList; */
|
||||
@ -447,75 +387,10 @@ nsSVGPathElement::IsAttributeMapped(const nsIAtom* name) const
|
||||
nsSVGPathElementBase::IsAttributeMapped(name);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathElement::BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
const nsAString* aValue, PRBool aNotify)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::d) {
|
||||
if (mSegments) {
|
||||
NS_REMOVE_SVGVALUE_OBSERVER(mSegments);
|
||||
mSegments = nsnull;
|
||||
}
|
||||
|
||||
if (aValue) {
|
||||
nsSVGPathDataParserToInternal parser(&mPathData);
|
||||
nsresult rv = parser.Parse(*aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReportAttributeParseFailure(GetOwnerDoc(), aName, *aValue);
|
||||
}
|
||||
} else {
|
||||
mPathData.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
return nsSVGPathElementBase::BeforeSetAttr(aNamespaceID, aName,
|
||||
aValue, aNotify);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGPathElement::WillModifySVGObservable(nsISVGValue* observable,
|
||||
nsISVGValue::modificationType aModType)
|
||||
{
|
||||
nsCOMPtr<nsIDOMSVGPathSegList> list = do_QueryInterface(observable);
|
||||
|
||||
if (list && mSegments == list) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return nsSVGPathElementBase::WillModifySVGObservable(observable, aModType);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGPathElement::DidModifySVGObservable(nsISVGValue* observable,
|
||||
nsISVGValue::modificationType aModType)
|
||||
{
|
||||
nsCOMPtr<nsIDOMSVGPathSegList> list = do_QueryInterface(observable);
|
||||
|
||||
if (list && mSegments == list) {
|
||||
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
|
||||
nsAutoString d;
|
||||
nsresult rv = value->GetValueString(d);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Want to keep the seglist alive - SetAttr normally invalidates it
|
||||
nsCOMPtr<nsIDOMSVGPathSegList> deathGrip = mSegments;
|
||||
mSegments = nsnull;
|
||||
|
||||
rv = SetAttr(kNameSpaceID_None, nsGkAtoms::d, d, PR_TRUE);
|
||||
|
||||
// Restore seglist
|
||||
mSegments = deathGrip;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return nsSVGPathElementBase::DidModifySVGObservable(observable, aModType);
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFlattenedPath>
|
||||
nsSVGPathElement::GetFlattenedPath(const gfxMatrix &aMatrix)
|
||||
{
|
||||
return mPathData.GetFlattenedPath(aMatrix);
|
||||
return mD.GetAnimValue().ToFlattenedPath(aMatrix);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@ -537,517 +412,15 @@ nsSVGPathElement::IsMarkable()
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
static double
|
||||
CalcVectorAngle(double ux, double uy, double vx, double vy)
|
||||
{
|
||||
double ta = atan2(uy, ux);
|
||||
double tb = atan2(vy, vx);
|
||||
if (tb >= ta)
|
||||
return tb-ta;
|
||||
return 2 * M_PI - (ta-tb);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
|
||||
{
|
||||
if (NS_FAILED(CreatePathSegList()))
|
||||
return;
|
||||
|
||||
PRUint32 count;
|
||||
mSegments->GetNumberOfItems(&count);
|
||||
nsCOMPtr<nsIDOMSVGPathSeg> segment;
|
||||
|
||||
float cx = 0.0f; // current point
|
||||
float cy = 0.0f;
|
||||
|
||||
float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
|
||||
float cy1 = 0.0f;
|
||||
|
||||
PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
|
||||
float px = 0, py = 0; // subpath initial point
|
||||
float pathAngle = 0;
|
||||
PRUint32 pathIndex = 0;
|
||||
|
||||
float prevAngle = 0, startAngle = 0, endAngle = 0;
|
||||
|
||||
PRBool newSegment = PR_FALSE;
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
nsCOMPtr<nsIDOMSVGPathSeg> segment;
|
||||
mSegments->GetItem(i, getter_AddRefs(segment));
|
||||
|
||||
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
|
||||
segment->GetPathSegType(&type);
|
||||
|
||||
float x, y;
|
||||
PRBool absCoords = PR_FALSE;
|
||||
|
||||
switch (type) {
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
|
||||
{
|
||||
x = px;
|
||||
y = py;
|
||||
startAngle = endAngle = atan2(y - cy, x - cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
|
||||
{
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(moveseg, "interface not implemented");
|
||||
moveseg->GetX(&x);
|
||||
moveseg->GetY(&y);
|
||||
x += cx;
|
||||
y += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(moveseg, "interface not implemented");
|
||||
moveseg->GetX(&x);
|
||||
moveseg->GetY(&y);
|
||||
}
|
||||
px = x;
|
||||
py = y;
|
||||
startAngle = endAngle = prevAngle;
|
||||
newSegment = PR_TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
|
||||
{
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(lineseg, "interface not implemented");
|
||||
lineseg->GetX(&x);
|
||||
lineseg->GetY(&y);
|
||||
x += cx;
|
||||
y += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(lineseg, "interface not implemented");
|
||||
lineseg->GetX(&x);
|
||||
lineseg->GetY(&y);
|
||||
}
|
||||
startAngle = endAngle = atan2(y - cy, x - cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
|
||||
{
|
||||
float x1, y1, x2, y2;
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
curveseg->GetX1(&x1);
|
||||
curveseg->GetY1(&y1);
|
||||
curveseg->GetX2(&x2);
|
||||
curveseg->GetY2(&y2);
|
||||
x += cx;
|
||||
y += cy;
|
||||
x1 += cx;
|
||||
y1 += cy;
|
||||
x2 += cx;
|
||||
y2 += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
curveseg->GetX1(&x1);
|
||||
curveseg->GetY1(&y1);
|
||||
curveseg->GetX2(&x2);
|
||||
curveseg->GetY2(&y2);
|
||||
}
|
||||
|
||||
cx1 = x2;
|
||||
cy1 = y2;
|
||||
|
||||
if (x1 == cx && y1 == cy) {
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
|
||||
if (x2 == x && y2 == y) {
|
||||
x2 = x1;
|
||||
y2 = y1;
|
||||
}
|
||||
|
||||
startAngle = atan2(y1 - cy, x1 - cx);
|
||||
endAngle = atan2(y - y2, x - x2);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
{
|
||||
float x1, y1;
|
||||
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
curveseg->GetX1(&x1);
|
||||
curveseg->GetY1(&y1);
|
||||
x += cx;
|
||||
y += cy;
|
||||
x1 += cx;
|
||||
y1 += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
curveseg->GetX1(&x1);
|
||||
curveseg->GetY1(&y1);
|
||||
}
|
||||
|
||||
cx1 = x1;
|
||||
cy1 = y1;
|
||||
|
||||
startAngle = atan2(y1 - cy, x1 - cx);
|
||||
endAngle = atan2(y - y1, x - x1);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
|
||||
{
|
||||
float r1, r2, angle;
|
||||
PRBool largeArcFlag, sweepFlag;
|
||||
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(arcseg, "interface not implemented");
|
||||
arcseg->GetX(&x);
|
||||
arcseg->GetY(&y);
|
||||
arcseg->GetR1(&r1);
|
||||
arcseg->GetR2(&r2);
|
||||
arcseg->GetAngle(&angle);
|
||||
arcseg->GetLargeArcFlag(&largeArcFlag);
|
||||
arcseg->GetSweepFlag(&sweepFlag);
|
||||
|
||||
x += cx;
|
||||
y += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(arcseg, "interface not implemented");
|
||||
arcseg->GetX(&x);
|
||||
arcseg->GetY(&y);
|
||||
arcseg->GetR1(&r1);
|
||||
arcseg->GetR2(&r2);
|
||||
arcseg->GetAngle(&angle);
|
||||
arcseg->GetLargeArcFlag(&largeArcFlag);
|
||||
arcseg->GetSweepFlag(&sweepFlag);
|
||||
}
|
||||
|
||||
/* check for degenerate ellipse */
|
||||
if (r1 == 0.0 || r2 == 0.0) {
|
||||
startAngle = endAngle = atan2(y - cy, x - cx);
|
||||
break;
|
||||
}
|
||||
|
||||
r1 = fabs(r1); r2 = fabs(r2);
|
||||
|
||||
float xp, yp, cxp, cyp;
|
||||
|
||||
/* slope fun&games ... see SVG spec, section F.6 */
|
||||
angle = angle*M_PI/180.0;
|
||||
xp = cos(angle)*(cx-x)/2.0 + sin(angle)*(cy-y)/2.0;
|
||||
yp = -sin(angle)*(cx-x)/2.0 + cos(angle)*(cy-y)/2.0;
|
||||
|
||||
/* make sure radii are large enough */
|
||||
float root, numerator = r1*r1*r2*r2 - r1*r1*yp*yp - r2*r2*xp*xp;
|
||||
if (numerator < 0.0) {
|
||||
float s = sqrt(1.0 - numerator/(r1*r1*r2*r2));
|
||||
r1 *= s;
|
||||
r2 *= s;
|
||||
root = 0.0;
|
||||
} else {
|
||||
root = sqrt(numerator/(r1*r1*yp*yp + r2*r2*xp*xp));
|
||||
if (largeArcFlag == sweepFlag)
|
||||
root = -root;
|
||||
}
|
||||
cxp = root*r1*yp/r2;
|
||||
cyp = -root*r2*xp/r1;
|
||||
|
||||
float theta, delta;
|
||||
theta = CalcVectorAngle(1.0, 0.0, (xp-cxp)/r1, (yp-cyp)/r2);
|
||||
delta = CalcVectorAngle((xp-cxp)/r1, (yp-cyp)/r2,
|
||||
(-xp-cxp)/r1, (-yp-cyp)/r2);
|
||||
if (!sweepFlag && delta > 0)
|
||||
delta -= 2.0*M_PI;
|
||||
else if (sweepFlag && delta < 0)
|
||||
delta += 2.0*M_PI;
|
||||
|
||||
float tx1, ty1, tx2, ty2;
|
||||
tx1 = -cos(angle)*r1*sin(theta) - sin(angle)*r2*cos(theta);
|
||||
ty1 = -sin(angle)*r1*sin(theta) + cos(angle)*r2*cos(theta);
|
||||
tx2 = -cos(angle)*r1*sin(theta+delta) - sin(angle)*r2*cos(theta+delta);
|
||||
ty2 = -sin(angle)*r1*sin(theta+delta) + cos(angle)*r2*cos(theta+delta);
|
||||
|
||||
if (delta < 0.0f) {
|
||||
tx1 = -tx1;
|
||||
ty1 = -ty1;
|
||||
tx2 = -tx2;
|
||||
ty2 = -ty2;
|
||||
}
|
||||
|
||||
startAngle = atan2(ty1, tx1);
|
||||
endAngle = atan2(ty2, tx2);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
|
||||
{
|
||||
y = cy;
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(lineseg, "interface not implemented");
|
||||
lineseg->GetX(&x);
|
||||
x += cx;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(lineseg, "interface not implemented");
|
||||
lineseg->GetX(&x);
|
||||
}
|
||||
startAngle = endAngle = atan2(0, x - cx);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
|
||||
{
|
||||
x = cx;
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(lineseg, "interface not implemented");
|
||||
lineseg->GetY(&y);
|
||||
y += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(lineseg, "interface not implemented");
|
||||
lineseg->GetY(&y);
|
||||
}
|
||||
startAngle = endAngle = atan2(y - cy, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||
{
|
||||
float x1, y1, x2, y2;
|
||||
|
||||
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
|
||||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
|
||||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
|
||||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
|
||||
// the first controlpoint is the reflection of the last one about the current point:
|
||||
x1 = 2*cx - cx1;
|
||||
y1 = 2*cy - cy1;
|
||||
}
|
||||
else {
|
||||
// the first controlpoint is equal to the current point:
|
||||
x1 = cx;
|
||||
y1 = cy;
|
||||
}
|
||||
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
curveseg->GetX2(&x2);
|
||||
curveseg->GetY2(&y2);
|
||||
x += cx;
|
||||
y += cy;
|
||||
x2 += cx;
|
||||
y2 += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
curveseg->GetX2(&x2);
|
||||
curveseg->GetY2(&y2);
|
||||
}
|
||||
|
||||
cx1 = x2;
|
||||
cy1 = y2;
|
||||
|
||||
if (x1 == cx && y1 == cy) {
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
}
|
||||
|
||||
if (x2 == x && y2 == y) {
|
||||
x2 = x1;
|
||||
y2 = y1;
|
||||
}
|
||||
|
||||
startAngle = atan2(y1 - cy, x1 - cx);
|
||||
endAngle = atan2(y - y2, x - x2);
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||
absCoords = PR_TRUE;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||
{
|
||||
float x1, y1;
|
||||
|
||||
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
|
||||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
|
||||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
|
||||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
|
||||
// the first controlpoint is the reflection of the last one about the current point:
|
||||
x1 = 2*cx - cx1;
|
||||
y1 = 2*cy - cy1;
|
||||
}
|
||||
else {
|
||||
// the first controlpoint is equal to the current point:
|
||||
x1 = cx;
|
||||
y1 = cy;
|
||||
}
|
||||
|
||||
if (!absCoords) {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
x += cx;
|
||||
y += cy;
|
||||
} else {
|
||||
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
|
||||
NS_ASSERTION(curveseg, "interface not implemented");
|
||||
curveseg->GetX(&x);
|
||||
curveseg->GetY(&y);
|
||||
}
|
||||
|
||||
cx1 = x1;
|
||||
cy1 = y1;
|
||||
|
||||
startAngle = atan2(y1 - cy, x1 - cx);
|
||||
endAngle = atan2(y - y1, x - x1);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_ASSERTION(1==0, "unknown path segment");
|
||||
break;
|
||||
}
|
||||
lastSegmentType = type;
|
||||
|
||||
if (newSegment &&
|
||||
type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
|
||||
type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
|
||||
pathIndex = aMarks->Length() - 1;
|
||||
pathAngle = startAngle;
|
||||
aMarks->ElementAt(pathIndex).angle = pathAngle;
|
||||
newSegment = PR_FALSE;
|
||||
prevAngle = endAngle;
|
||||
} else if (type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS ||
|
||||
type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
|
||||
if (aMarks->Length())
|
||||
aMarks->ElementAt(aMarks->Length() - 1).angle = prevAngle;
|
||||
} else {
|
||||
aMarks->ElementAt(aMarks->Length() - 1).angle =
|
||||
nsSVGUtils::AngleBisect(prevAngle, startAngle);
|
||||
prevAngle = endAngle;
|
||||
}
|
||||
|
||||
aMarks->AppendElement(nsSVGMark(x, y, 0));
|
||||
|
||||
if (type == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) {
|
||||
prevAngle = nsSVGUtils::AngleBisect(endAngle, pathAngle);
|
||||
aMarks->ElementAt(pathIndex).angle = prevAngle;
|
||||
}
|
||||
|
||||
cx = x;
|
||||
cy = y;
|
||||
}
|
||||
|
||||
if (aMarks->Length())
|
||||
aMarks->ElementAt(aMarks->Length() - 1).angle = prevAngle;
|
||||
mD.GetAnimValue().GetMarkerPositioningData(aMarks);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathElement::ConstructPath(gfxContext *aCtx)
|
||||
{
|
||||
mPathData.Playback(aCtx);
|
||||
mD.GetAnimValue().ConstructPath(aCtx);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
// nsSVGPathList
|
||||
|
||||
void
|
||||
nsSVGPathList::Clear()
|
||||
{
|
||||
if (mArguments) {
|
||||
free(mArguments);
|
||||
mArguments = nsnull;
|
||||
}
|
||||
mNumCommands = 0;
|
||||
mNumArguments = 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathList::Playback(gfxContext *aCtx)
|
||||
{
|
||||
float *args = mArguments;
|
||||
for (PRUint32 i = 0; i < mNumCommands; i++) {
|
||||
PRUint8 command =
|
||||
reinterpret_cast<PRUint8*>(mArguments + mNumArguments)[i / 4];
|
||||
command = (command >> (2 * (i % 4))) & 0x3;
|
||||
switch (command) {
|
||||
case MOVETO:
|
||||
aCtx->MoveTo(gfxPoint(args[0], args[1]));
|
||||
args += 2;
|
||||
break;
|
||||
case LINETO:
|
||||
aCtx->LineTo(gfxPoint(args[0], args[1]));
|
||||
args += 2;
|
||||
break;
|
||||
case CURVETO:
|
||||
aCtx->CurveTo(gfxPoint(args[0], args[1]),
|
||||
gfxPoint(args[2], args[3]),
|
||||
gfxPoint(args[4], args[5]));
|
||||
args += 6;
|
||||
break;
|
||||
case CLOSEPATH:
|
||||
aCtx->ClosePath();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFlattenedPath>
|
||||
nsSVGPathList::GetFlattenedPath(const gfxMatrix& aMatrix)
|
||||
{
|
||||
nsRefPtr<gfxContext> ctx =
|
||||
new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
|
||||
|
||||
ctx->SetMatrix(aMatrix);
|
||||
Playback(ctx);
|
||||
ctx->IdentityMatrix();
|
||||
|
||||
return ctx->GetFlattenedPath();
|
||||
}
|
||||
|
@ -43,28 +43,11 @@
|
||||
#include "nsIDOMSVGPathElement.h"
|
||||
#include "nsIDOMSVGAnimatedPathData.h"
|
||||
#include "nsSVGNumber2.h"
|
||||
#include "SVGAnimatedPathSegList.h"
|
||||
#include "gfxPath.h"
|
||||
|
||||
class gfxContext;
|
||||
|
||||
class nsSVGPathList
|
||||
{
|
||||
friend class nsSVGPathDataParserToInternal;
|
||||
|
||||
public:
|
||||
enum { MOVETO, LINETO, CURVETO, CLOSEPATH };
|
||||
nsSVGPathList() : mArguments(nsnull), mNumCommands(0), mNumArguments(0) {}
|
||||
~nsSVGPathList() { Clear(); }
|
||||
void Playback(gfxContext *aCtx);
|
||||
already_AddRefed<gfxFlattenedPath> GetFlattenedPath(const gfxMatrix &aMatrix);
|
||||
void Clear();
|
||||
|
||||
protected:
|
||||
float *mArguments;
|
||||
PRUint32 mNumCommands;
|
||||
PRUint32 mNumArguments;
|
||||
};
|
||||
|
||||
typedef nsSVGPathGeometryElement nsSVGPathElementBase;
|
||||
|
||||
class nsSVGPathElement : public nsSVGPathElementBase,
|
||||
@ -78,9 +61,9 @@ protected:
|
||||
friend nsresult NS_NewSVGPathElement(nsIContent **aResult,
|
||||
already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
nsSVGPathElement(already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
virtual ~nsSVGPathElement();
|
||||
|
||||
public:
|
||||
typedef mozilla::SVGAnimatedPathSegList SVGAnimatedPathSegList;
|
||||
// interfaces:
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
@ -95,12 +78,6 @@ public:
|
||||
// nsIContent interface
|
||||
NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* name) const;
|
||||
|
||||
// nsISVGValueObserver
|
||||
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
|
||||
nsISVGValue::modificationType aModType);
|
||||
NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
|
||||
nsISVGValue::modificationType aModType);
|
||||
|
||||
// nsSVGPathGeometryElement methods:
|
||||
virtual PRBool AttributeDefinesGeometry(const nsIAtom *aName);
|
||||
virtual PRBool IsMarkable();
|
||||
@ -111,22 +88,25 @@ public:
|
||||
|
||||
// nsIContent interface
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
const nsAString* aValue, PRBool aNotify);
|
||||
|
||||
virtual nsXPCClassInfo* GetClassInfo();
|
||||
|
||||
virtual SVGAnimatedPathSegList* GetAnimPathSegList() {
|
||||
return &mD;
|
||||
}
|
||||
|
||||
virtual nsIAtom* GetPathDataAttrName() const {
|
||||
return nsGkAtoms::d;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// nsSVGElement method
|
||||
virtual NumberAttributesInfo GetNumberInfo();
|
||||
|
||||
// Helper for lazily creating pathseg list
|
||||
nsresult CreatePathSegList();
|
||||
|
||||
nsCOMPtr<nsIDOMSVGPathSegList> mSegments;
|
||||
SVGAnimatedPathSegList mD;
|
||||
nsSVGNumber2 mPathLength;
|
||||
static NumberInfo sNumberInfo;
|
||||
nsSVGPathList mPathData;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,173 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Mozilla SVG project.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Crocodile Clips Ltd..
|
||||
* Portions created by the Initial Developer are Copyright (C) 2001
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __NS_SVGPATHSEG_H__
|
||||
#define __NS_SVGPATHSEG_H__
|
||||
|
||||
#include "nsIDOMSVGPathSeg.h"
|
||||
#include "nsIWeakReference.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "nsSVGPathDataParser.h"
|
||||
|
||||
// 0dfd1b3c-5638-4813-a6f8-5a325a35d06e
|
||||
#define NS_SVGPATHSEG_IID \
|
||||
{ 0x0dfd1b3c, 0x5638, 0x4813, \
|
||||
{ 0xa6, 0xf8, 0x5a, 0x32, 0x5a, 0x35, 0xd0, 0x6e } }
|
||||
|
||||
#define NS_ENSURE_NATIVE_PATH_SEG(obj, retval) \
|
||||
{ \
|
||||
nsCOMPtr<nsSVGPathSeg> path = do_QueryInterface(obj); \
|
||||
if (!path) { \
|
||||
*retval = nsnull; \
|
||||
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR; \
|
||||
} \
|
||||
}
|
||||
|
||||
class nsSVGPathSegList;
|
||||
class nsISVGValue;
|
||||
|
||||
struct nsSVGPathSegTraversalState {
|
||||
float curPosX, startPosX, quadCPX, cubicCPX;
|
||||
float curPosY, startPosY, quadCPY, cubicCPY;
|
||||
nsSVGPathSegTraversalState()
|
||||
{
|
||||
curPosX = startPosX = quadCPX = cubicCPX = 0;
|
||||
curPosY = startPosY = quadCPY = cubicCPY = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class nsSVGPathSeg : public nsIDOMSVGPathSeg
|
||||
{
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_SVGPATHSEG_IID)
|
||||
|
||||
nsSVGPathSeg() : mCurrentList(nsnull) {}
|
||||
nsresult SetCurrentList(nsISVGValue* aList);
|
||||
nsQueryReferent GetCurrentList() const;
|
||||
|
||||
// nsISupports interface:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIDOMSVGPathSeg interface:
|
||||
NS_IMETHOD GetPathSegType(PRUint16 *aPathSegType) = 0;
|
||||
NS_IMETHOD GetPathSegTypeAsLetter(nsAString & aPathSegTypeAsLetter) = 0;
|
||||
|
||||
// nsSVGPathSeg methods:
|
||||
NS_IMETHOD GetValueString(nsAString& aValue) = 0;
|
||||
virtual float GetLength(nsSVGPathSegTraversalState *ts) = 0;
|
||||
|
||||
protected:
|
||||
void DidModify();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIWeakReference> mCurrentList;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsSVGPathSeg, NS_SVGPATHSEG_IID)
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegClosePath();
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegMovetoAbs(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegMovetoRel(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoAbs(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoRel(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicAbs(float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicRel(float x, float y,
|
||||
float x1, float y1,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticAbs(float x, float y,
|
||||
float x1, float y1);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticRel(float x, float y,
|
||||
float x1, float y1);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegArcAbs(float x, float y,
|
||||
float r1, float r2, float angle,
|
||||
PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegArcRel(float x, float y,
|
||||
float r1, float r2, float angle,
|
||||
PRBool largeArcFlag, PRBool sweepFlag);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoHorizontalAbs(float x);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoHorizontalRel(float x);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoVerticalAbs(float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegLinetoVerticalRel(float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicSmoothAbs(float x, float y,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoCubicSmoothRel(float x, float y,
|
||||
float x2, float y2);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y);
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
NS_NewSVGPathSegCurvetoQuadraticSmoothRel(float x, float y);
|
||||
|
||||
|
||||
#endif //__NS_SVGPATHSEG_H__
|
@ -1,400 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Mozilla SVG project.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Crocodile Clips Ltd..
|
||||
* Portions created by the Initial Developer are Copyright (C) 2001
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsSVGPathSegList.h"
|
||||
#include "nsSVGPathSeg.h"
|
||||
#include "nsSVGValue.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsDOMError.h"
|
||||
#include "nsSVGPathDataParser.h"
|
||||
#include "nsReadableUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsSVGPathSegList
|
||||
|
||||
class nsSVGPathSegList : public nsIDOMSVGPathSegList,
|
||||
public nsSVGValue,
|
||||
public nsISVGValueObserver
|
||||
{
|
||||
protected:
|
||||
friend nsresult NS_NewSVGPathSegList(nsIDOMSVGPathSegList** result);
|
||||
|
||||
nsSVGPathSegList();
|
||||
~nsSVGPathSegList();
|
||||
// void Init();
|
||||
|
||||
public:
|
||||
// nsISupports interface:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIDOMSVGPathSegList interface:
|
||||
NS_DECL_NSIDOMSVGPATHSEGLIST
|
||||
|
||||
// remainder of nsISVGValue interface:
|
||||
NS_IMETHOD SetValueString(const nsAString& aValue);
|
||||
NS_IMETHOD GetValueString(nsAString& aValue);
|
||||
|
||||
// nsISVGValueObserver
|
||||
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
|
||||
modificationType aModType);
|
||||
NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
|
||||
modificationType aModType);
|
||||
|
||||
// nsISupportsWeakReference
|
||||
// implementation inherited from nsSupportsWeakReference
|
||||
|
||||
protected:
|
||||
// implementation helpers:
|
||||
void AppendElement(nsSVGPathSeg* aElement);
|
||||
void RemoveElementAt(PRInt32 index);
|
||||
void InsertElementAt(nsSVGPathSeg* aElement, PRInt32 index);
|
||||
void RemoveFromCurrentList(nsSVGPathSeg*);
|
||||
|
||||
void ReleaseSegments(PRBool aModify = PR_TRUE);
|
||||
|
||||
nsCOMArray<nsIDOMSVGPathSeg> mSegments;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Implementation
|
||||
|
||||
nsSVGPathSegList::nsSVGPathSegList()
|
||||
{
|
||||
}
|
||||
|
||||
nsSVGPathSegList::~nsSVGPathSegList()
|
||||
{
|
||||
PRInt32 count = mSegments.Count();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
nsSVGPathSeg* seg = static_cast<nsSVGPathSeg*>(mSegments.ObjectAt(i));
|
||||
seg->SetCurrentList(nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISupports methods:
|
||||
|
||||
NS_IMPL_ADDREF(nsSVGPathSegList)
|
||||
NS_IMPL_RELEASE(nsSVGPathSegList)
|
||||
|
||||
DOMCI_DATA(SVGPathSegList, nsSVGPathSegList)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsSVGPathSegList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISVGValue)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPathSegList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGPathSegList)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISVGValue)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISVGValue methods:
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGPathSegList::SetValueString(const nsAString& aValue)
|
||||
{
|
||||
WillModify();
|
||||
ReleaseSegments(PR_FALSE);
|
||||
nsSVGPathDataParserToDOM parser(&mSegments);
|
||||
nsresult rv = parser.Parse(aValue);
|
||||
|
||||
PRInt32 count = mSegments.Count();
|
||||
for (PRInt32 i=0; i<count; ++i) {
|
||||
nsSVGPathSeg* seg = static_cast<nsSVGPathSeg*>(mSegments.ObjectAt(i));
|
||||
seg->SetCurrentList(this);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ERROR("path data parse error!");
|
||||
ReleaseSegments(PR_FALSE);
|
||||
}
|
||||
DidModify();
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGPathSegList::GetValueString(nsAString& aValue)
|
||||
{
|
||||
aValue.Truncate();
|
||||
|
||||
PRInt32 count = mSegments.Count();
|
||||
|
||||
if (count<=0) return NS_OK;
|
||||
|
||||
PRInt32 i = 0;
|
||||
|
||||
while (1) {
|
||||
nsSVGPathSeg* seg = static_cast<nsSVGPathSeg*>(mSegments.ObjectAt(i));
|
||||
|
||||
nsAutoString str;
|
||||
seg->GetValueString(str);
|
||||
aValue.Append(str);
|
||||
|
||||
if (++i >= count) break;
|
||||
|
||||
aValue.AppendLiteral(" ");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMSVGPathSegList methods:
|
||||
|
||||
/* readonly attribute unsigned long numberOfItems; */
|
||||
NS_IMETHODIMP nsSVGPathSegList::GetNumberOfItems(PRUint32 *aNumberOfItems)
|
||||
{
|
||||
*aNumberOfItems = mSegments.Count();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void clear (); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::Clear()
|
||||
{
|
||||
WillModify();
|
||||
ReleaseSegments();
|
||||
DidModify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIDOMSVGPathSeg initialize (in nsIDOMSVGPathSeg newItem); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::Initialize(nsIDOMSVGPathSeg *newItem,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
NS_ENSURE_NATIVE_PATH_SEG(newItem, _retval);
|
||||
Clear();
|
||||
return AppendItem(newItem, _retval);
|
||||
}
|
||||
|
||||
/* nsIDOMSVGPathSeg getItem (in unsigned long index); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::GetItem(PRUint32 index, nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
if (index >= static_cast<PRUint32>(mSegments.Count())) {
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
*_retval = mSegments.ObjectAt(index);
|
||||
NS_ADDREF(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIDOMSVGPathSeg insertItemBefore (in nsIDOMSVGPathSeg newItem, in unsigned long index); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::InsertItemBefore(nsIDOMSVGPathSeg *newItem,
|
||||
PRUint32 index,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
NS_ENSURE_NATIVE_PATH_SEG(newItem, _retval);
|
||||
|
||||
if (index >= static_cast<PRUint32>(mSegments.Count())) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
InsertElementAt(static_cast<nsSVGPathSeg*>(newItem), index);
|
||||
NS_ADDREF(*_retval = newItem);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIDOMSVGPathSeg replaceItem (in nsIDOMSVGPathSeg newItem, in unsigned long index); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::ReplaceItem(nsIDOMSVGPathSeg *newItem,
|
||||
PRUint32 index,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
NS_ENSURE_NATIVE_PATH_SEG(newItem, _retval);
|
||||
|
||||
// immediately remove the new item from its current list
|
||||
nsSVGPathSeg* newItemSeg = static_cast<nsSVGPathSeg*>(newItem);
|
||||
RemoveFromCurrentList(newItemSeg);
|
||||
|
||||
if (index >= static_cast<PRUint32>(mSegments.Count())) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
// NOTE: the new item can never be the item we will be replacing now that we removed it from its current list beforehand
|
||||
InsertElementAt(newItemSeg, index);
|
||||
RemoveFromCurrentList(static_cast<nsSVGPathSeg*>(mSegments.ObjectAt(index+1)));
|
||||
NS_ADDREF(*_retval = newItem);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIDOMSVGPathSeg removeItem (in unsigned long index); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::RemoveItem(PRUint32 index, nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
if (index >= static_cast<PRUint32>(mSegments.Count())) {
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
|
||||
*_retval = mSegments.ObjectAt(index);
|
||||
NS_ADDREF(*_retval);
|
||||
WillModify();
|
||||
RemoveElementAt(index);
|
||||
DidModify();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIDOMSVGPathSeg appendItem (in nsIDOMSVGPathSeg newItem); */
|
||||
NS_IMETHODIMP nsSVGPathSegList::AppendItem(nsIDOMSVGPathSeg *newItem,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
NS_ENSURE_NATIVE_PATH_SEG(newItem, _retval);
|
||||
NS_ADDREF(*_retval = newItem);
|
||||
AppendElement(static_cast<nsSVGPathSeg*>(newItem));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISVGValueObserver methods
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGPathSegList::WillModifySVGObservable(nsISVGValue* observable,
|
||||
modificationType aModType)
|
||||
{
|
||||
// This method is not yet used
|
||||
|
||||
NS_NOTYETIMPLEMENTED("nsSVGPathSegList::WillModifySVGObservable");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGPathSegList::DidModifySVGObservable (nsISVGValue* observable,
|
||||
modificationType aModType)
|
||||
{
|
||||
// This method is not yet used
|
||||
|
||||
NS_NOTYETIMPLEMENTED("nsSVGPathSegList::DidModifySVGObservable");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Implementation helpers
|
||||
|
||||
void
|
||||
nsSVGPathSegList::ReleaseSegments(PRBool aModify)
|
||||
{
|
||||
if (aModify) {
|
||||
WillModify();
|
||||
}
|
||||
PRInt32 count = mSegments.Count();
|
||||
for (PRInt32 i = 0; i < count; ++i) {
|
||||
nsSVGPathSeg* seg = static_cast<nsSVGPathSeg*>(mSegments.ObjectAt(i));
|
||||
seg->SetCurrentList(nsnull);
|
||||
}
|
||||
mSegments.Clear();
|
||||
if (aModify) {
|
||||
DidModify();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathSegList::AppendElement(nsSVGPathSeg* aElement)
|
||||
{
|
||||
WillModify();
|
||||
// XXX: we should only remove an item from its current list if we
|
||||
// successfully added it to this list
|
||||
RemoveFromCurrentList(aElement);
|
||||
mSegments.AppendObject(aElement);
|
||||
aElement->SetCurrentList(this);
|
||||
DidModify();
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathSegList::RemoveElementAt(PRInt32 index)
|
||||
{
|
||||
WillModify();
|
||||
nsSVGPathSeg* seg = static_cast<nsSVGPathSeg*>(mSegments.ObjectAt(index));
|
||||
seg->SetCurrentList(nsnull);
|
||||
mSegments.RemoveObjectAt(index);
|
||||
DidModify();
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGPathSegList::InsertElementAt(nsSVGPathSeg* aElement, PRInt32 index)
|
||||
{
|
||||
WillModify();
|
||||
// XXX: we should only remove an item from its current list if we
|
||||
// successfully added it to this list
|
||||
RemoveFromCurrentList(aElement);
|
||||
mSegments.InsertObjectAt(aElement, index);
|
||||
aElement->SetCurrentList(this);
|
||||
DidModify();
|
||||
}
|
||||
|
||||
// The SVG specs state that 'if newItem is already in a list, it
|
||||
// is removed from its previous list before it is inserted into this
|
||||
// list'. This is a helper function to do that.
|
||||
void
|
||||
nsSVGPathSegList::RemoveFromCurrentList(nsSVGPathSeg* aSeg)
|
||||
{
|
||||
nsCOMPtr<nsISVGValue> currentList = aSeg->GetCurrentList();
|
||||
if (currentList) {
|
||||
// aSeg's current list must be cast back to a nsSVGPathSegList*
|
||||
nsSVGPathSegList* otherSegList = static_cast<nsSVGPathSegList*>(currentList.get());
|
||||
PRInt32 ix = otherSegList->mSegments.IndexOfObject(aSeg);
|
||||
if (ix != -1) {
|
||||
otherSegList->RemoveElementAt(ix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Exported creation functions:
|
||||
|
||||
nsresult
|
||||
NS_NewSVGPathSegList(nsIDOMSVGPathSegList** result)
|
||||
{
|
||||
*result = nsnull;
|
||||
|
||||
nsSVGPathSegList* pathSegList = new nsSVGPathSegList();
|
||||
if (!pathSegList) return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(pathSegList);
|
||||
|
||||
// pathSegList->Init();
|
||||
|
||||
*result = (nsIDOMSVGPathSegList*) pathSegList;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1035,6 +1035,7 @@ nsSVGSVGElement::BindToTree(nsIDocument* aDocument,
|
||||
rv = mTimedDocumentRoot->SetParent(smilController);
|
||||
if (mStartAnimationOnBindToTree) {
|
||||
mTimedDocumentRoot->Begin();
|
||||
mStartAnimationOnBindToTree = PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,9 +107,11 @@ nsSVGTransformSMILAttr::GetBaseValue() const
|
||||
void
|
||||
nsSVGTransformSMILAttr::ClearAnimValue()
|
||||
{
|
||||
mVal->WillModify(nsISVGValue::mod_other);
|
||||
PRBool animValSet = !!mVal->mAnimVal;
|
||||
mVal->mAnimVal = nsnull;
|
||||
mVal->DidModify(nsISVGValue::mod_other);
|
||||
if (animValSet) {
|
||||
mSVGElement->DidAnimateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -14,6 +14,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=515116
|
||||
<div id="content" style="display:none;">
|
||||
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
|
||||
<text id="text">text</text>
|
||||
<path id="path"/>
|
||||
</svg>
|
||||
</div>
|
||||
<pre id="test">
|
||||
@ -121,6 +122,7 @@ var tests = [
|
||||
prop_name: 'tableValues',
|
||||
bv_name: 'baseVal',
|
||||
av_name: 'animVal',
|
||||
el_type: 'xxx',
|
||||
prop_type: 'SVGAnimatedNumberList',
|
||||
list_type: 'SVGNumberList',
|
||||
item_type: 'SVGNumber',
|
||||
@ -138,6 +140,7 @@ var tests = [
|
||||
prop_name: null, // SVGAnimatedPoints is an inherited interface!
|
||||
bv_name: 'points',
|
||||
av_name: 'animatedPoints',
|
||||
el_type: 'SVGPolylineElement',
|
||||
prop_type: null,
|
||||
list_type: 'SVGPointList',
|
||||
item_type: 'SVGPoint',
|
||||
@ -148,6 +151,7 @@ var tests = [
|
||||
attr_val_5b: '',
|
||||
item_constructor: document.getElementById('svg').createSVGPoint
|
||||
},
|
||||
*/
|
||||
{
|
||||
// SVGPathSegList test:
|
||||
target_element_id: 'path',
|
||||
@ -155,19 +159,21 @@ var tests = [
|
||||
prop_name: null, // SVGAnimatedPathData is an inherited interface!
|
||||
bv_name: 'pathSegList',
|
||||
av_name: 'animatedPathSegList',
|
||||
el_type: 'SVGPathElement',
|
||||
prop_type: null,
|
||||
list_type: 'SVGPathSegList',
|
||||
item_type: 'SVGPathSeg',
|
||||
attr_val_3a: '',
|
||||
attr_val_3b: '',
|
||||
attr_val_4 : '',
|
||||
attr_val_5a: '',
|
||||
attr_val_5b: '',
|
||||
attr_val_3a: 'M 10,10 L 50,50 L 90,10',
|
||||
attr_val_3b: 'M 10,50 L 50,10 L 90,50',
|
||||
attr_val_4 : 'M 10,10 L 50,50 L 90,10 M 200,100',
|
||||
attr_val_5a: 'M 10,10 L 50,50 L 90,10 L 130,50 L 170,10',
|
||||
attr_val_5b: 'M 50,10 L 50,10 L 90,50 L 130,10 L 170,50',
|
||||
item_constructor: function() {
|
||||
// XXX return different values each time
|
||||
return SVGPathElement.createSVGPathSegLinetoAbs(1, 1);
|
||||
return document.getElementById('path').createSVGPathSegMovetoAbs(1, 1);
|
||||
}
|
||||
},
|
||||
/*
|
||||
{
|
||||
// SVGPathSegList test:
|
||||
target_element_id: 'path',
|
||||
@ -175,6 +181,7 @@ var tests = [
|
||||
prop_name: null, // SVGAnimatedPathData is an inherited interface!
|
||||
bv_name: 'normalizedPathSegList',
|
||||
av_name: 'animatedNormalizedPathSegList',
|
||||
el_type: 'SVGPathElement',
|
||||
prop_type: null,
|
||||
list_type: 'SVGPathSegList',
|
||||
item_type: 'SVGPathSeg',
|
||||
@ -195,6 +202,7 @@ var tests = [
|
||||
prop_name: null, // SVGStringList attributes are not animatable
|
||||
bv_name: 'requiredFeatures',
|
||||
av_name: null,
|
||||
el_type: 'SVGGElement',
|
||||
prop_type: null,
|
||||
list_type: 'SVGStringList',
|
||||
item_type: 'DOMString',
|
||||
@ -219,6 +227,7 @@ var tests = [
|
||||
prop_name: 'transform',
|
||||
bv_name: 'baseVal',
|
||||
av_name: 'animVal',
|
||||
el_type: 'SVGGElement',
|
||||
prop_type: 'SVGAnimatedTransformList',
|
||||
list_type: 'SVGTransformList',
|
||||
item_type: 'SVGTransform',
|
||||
@ -878,12 +887,15 @@ function run_animation_timeline_tests()
|
||||
'The start of the animation should have changed the number of items '+
|
||||
'in the '+t.list_type+' for '+t.bv_path+' to 5.');
|
||||
|
||||
// TODO
|
||||
if (t.list_type != 'SVGPathSegList') {
|
||||
ok(t.animVal.getItem(3) === t.old_animVal_items[3],
|
||||
'When affected by SMIL animation, list items in the '+t.list_type+
|
||||
' for '+t.bv_path+' that are at indexes that existed prior to the '+
|
||||
'start of the animation should be the exact same objects as the '+
|
||||
'objects that were at those indexes prior to the start of the '+
|
||||
'animation.');
|
||||
}
|
||||
|
||||
t.old_animVal_items = get_array_of_list_items(t.animVal);
|
||||
|
||||
@ -1042,11 +1054,13 @@ function run_animation_timeline_tests()
|
||||
|
||||
} else {
|
||||
|
||||
/* TODO
|
||||
ok(false,
|
||||
'Decide what to do here - see ' +
|
||||
'https://bugzilla.mozilla.org/show_bug.cgi?id=573716 - we ' +
|
||||
'probably should be discarding any animation sandwich layers from ' +
|
||||
'a layer that fails to add, on up.');
|
||||
*/
|
||||
|
||||
// In other words, we wouldn't need the if-else check here.
|
||||
|
||||
|
@ -51,10 +51,11 @@ interface nsIDocShell;
|
||||
|
||||
#define NS_SHISTORY_INTERNAL_CONTRACTID "@mozilla.org/browser/shistory-internal;1"
|
||||
|
||||
template<class E> class nsTArray;
|
||||
template<class E, class A> class nsTArray;
|
||||
struct nsTArrayDefaultAllocator;
|
||||
%}
|
||||
|
||||
[ref] native nsDocshellIDArray(nsTArray<PRUint64>);
|
||||
[ref] native nsDocshellIDArray(nsTArray<PRUint64, nsTArrayDefaultAllocator>);
|
||||
|
||||
[scriptable, uuid(2dede933-25e1-47a3-8f61-0127c785ea01)]
|
||||
interface nsISHistoryInternal: nsISupports
|
||||
|
@ -321,9 +321,9 @@ ContentChild::DeallocPExternalHelperApp(PExternalHelperAppChild* aService)
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvRegisterChrome(const nsTArray<ChromePackage>& packages,
|
||||
const nsTArray<ResourceMapping>& resources,
|
||||
const nsTArray<OverrideMapping>& overrides)
|
||||
ContentChild::RecvRegisterChrome(const InfallibleTArray<ChromePackage>& packages,
|
||||
const InfallibleTArray<ResourceMapping>& resources,
|
||||
const InfallibleTArray<OverrideMapping>& overrides)
|
||||
{
|
||||
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
|
||||
nsChromeRegistryContent* chromeRegistry =
|
||||
|
@ -94,9 +94,9 @@ public:
|
||||
const PRInt64& aContentLength);
|
||||
virtual bool DeallocPExternalHelperApp(PExternalHelperAppChild *aService);
|
||||
|
||||
virtual bool RecvRegisterChrome(const nsTArray<ChromePackage>& packages,
|
||||
const nsTArray<ResourceMapping>& resources,
|
||||
const nsTArray<OverrideMapping>& overrides);
|
||||
virtual bool RecvRegisterChrome(const InfallibleTArray<ChromePackage>& packages,
|
||||
const InfallibleTArray<ResourceMapping>& resources,
|
||||
const InfallibleTArray<OverrideMapping>& overrides);
|
||||
|
||||
virtual bool RecvSetOffline(const PRBool& offline);
|
||||
|
||||
@ -130,7 +130,7 @@ private:
|
||||
*/
|
||||
NS_NORETURN void QuickExit();
|
||||
|
||||
nsTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
|
||||
InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
|
||||
nsRefPtr<ConsoleListener> mConsoleListener;
|
||||
|
||||
static ContentChild* sSingleton;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user