Bug 337855 - Don't rebuild places menus on each open. patch by seth, dietrich & me, r=dietrich/me.

This commit is contained in:
mozilla.mano@sent.com 2007-06-15 18:53:06 -07:00
Родитель 395b420775
Коммит b61dfd1ce7
3 изменённых файлов: 415 добавлений и 198 удалений

Просмотреть файл

@ -290,20 +290,28 @@ var BookmarksEventHandler = {
if (target.localName == "menupopup" && if (target.localName == "menupopup" &&
target.id != "bookmarksMenuPopup" && target.id != "bookmarksMenuPopup" &&
target.getAttribute("anonid") != "chevronPopup") { target.getAttribute("anonid") != "chevronPopup") {
// Show "Open All in Tabs" menuitem if there are at least // Add the "Open All in Tabs" menuitem if there are
// two menuitems with places result nodes, and "Open (Feed Name)" // at least two menuitems with places result nodes.
// if it's a livemark with a siteURI. // Add the "Open (Feed Name)" menuitem if it's a livemark with a siteURI.
var numNodes = 0; var numNodes = 0;
var hasMultipleEntries = false; var hasMultipleEntries = false;
var hasFeedHomePage = false; var hasFeedHomePage = false;
var currentChild = target.firstChild; var currentChild = target.firstChild;
while (currentChild && numNodes < 2) { while (currentChild) {
if (currentChild.node && currentChild.localName == "menuitem") if (currentChild.localName == "menuitem" && currentChild.node)
numNodes++; numNodes++;
// If the menuitem already exists, do nothing.
if (currentChild.getAttribute("openInTabs") == "true")
return;
if (currentChild.hasAttribute("siteURI"))
return;
currentChild = currentChild.nextSibling; currentChild = currentChild.nextSibling;
} }
if (numNodes > 1) if (numNodes > 1)
hasMultipleEntries = true; hasMultipleEntries = true;
var button = target.parentNode; var button = target.parentNode;
if (button.getAttribute("livemark") == "true" && if (button.getAttribute("livemark") == "true" &&
button.hasAttribute("siteURI")) button.hasAttribute("siteURI"))

Просмотреть файл

@ -62,8 +62,11 @@
]]></constructor> ]]></constructor>
<destructor><![CDATA[ <destructor><![CDATA[
this._result = null;
this._resultNode = null; this._resultNode = null;
if (this._result) {
this._result.viewer = null;
this._result = null;
}
]]></destructor> ]]></destructor>
<method name="_init"> <method name="_init">
@ -88,11 +91,10 @@
<body><![CDATA[ <body><![CDATA[
if (!this._resultNode) if (!this._resultNode)
this._init(); this._init();
if (PlacesUtils.nodeIsContainer(this._resultNode)) {
this._resultNode.QueryInterface(Ci.nsINavHistoryContainerResultNode); if (!this._resultNode.containerOpen)
this._resultNode.containerOpen = true; this._resultNode.containerOpen = true;
}
this._rebuild();
if (this.popupShowingCallback) if (this.popupShowingCallback)
this.popupShowingCallback(); this.popupShowingCallback();
]]></body> ]]></body>
@ -181,48 +183,32 @@
]]></body> ]]></body>
</method> </method>
<method name="_rebuild"> <!-- Map for containerNodes<->domNodes. There's only one map per
result/viewer, the field is initialized once for the root menu,
for sub menus it is set to the root's map (see insertNewItem) -->
<field name="_containerNodesMap">null</field>
<method name="removeItem">
<parameter name="child"/>
<body><![CDATA[ <body><![CDATA[
// Make sure not to hold onto any references to menu nodes when we if (PlacesUtils.nodeIsContainer(child.node)) {
// rebuild, since rebuilding deletes all the nodes in the menu and for (var i=0; i < this._containerNodesMap.length; i++) {
// re-adds them. If we use a reference to a deleted node, all kinds if (this._containerNodesMap[i].resultNode == child.node)
// of exceptions and asserts will fire. this._containerNodesMap.splice(i, 1);
if (this._DNDObserver._overFolder.node) }
this._DNDObserver._clearOverFolder();
this._cleanMenu();
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
if (PlacesUtils.nodeIsContainer(this._resultNode))
this._resultNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
else
return; // Nothing to do if this menu isn't a container.
// Keep track of whether to make submenus for children
var noSubmenus = (this.getAttribute("nosubmenus") == "true");
// Container should always be open while rebuilding.
var wasOpen = this._resultNode.containerOpen;
this._resultNode.containerOpen = true;
#ifdef XP_MACOSX
// On Mac OSX, we need to manually attach the XBL binding for the bookmarks menu,
// because menus in the OSX menubar aren't real DOM nodes, and they don't get styles
// applied.
var needsBindingAttachment = false;
var currentNode = this.parentNode;
while (currentNode && !needsBindingAttachment) {
if (currentNode.id && currentNode.id == "bookmarksMenu")
needsBindingAttachment = true;
currentNode = currentNode.parentNode;
} }
#endif
var cc = this._resultNode.childCount; this.removeChild(child);
if (cc > 0) { ]]></body>
for (var i = 0; i < cc; ++i) { </method>
var child = this._resultNode.getChild(i);
<method name="insertNewItem">
<parameter name="child"/>
<parameter name="before"/>
<body><![CDATA[
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var element = null; var element = null;
if (PlacesUtils.nodeIsURI(child) || noSubmenus) { if (PlacesUtils.nodeIsURI(child)) {
element = document.createElementNS(XULNS, "menuitem"); element = document.createElementNS(XULNS, "menuitem");
element.setAttribute("label", child.title); element.setAttribute("label", child.title);
element.setAttribute("url", child.uri); element.setAttribute("url", child.uri);
@ -242,11 +228,9 @@
element.setAttribute("livemark", "true"); element.setAttribute("livemark", "true");
var folder = child.itemId; var folder = child.itemId;
var siteURI = PlacesUtils.livemarks.getSiteURI(folder); var siteURI = PlacesUtils.livemarks.getSiteURI(folder);
if (siteURI) { if (siteURI)
element.setAttribute("siteURI", siteURI.spec); element.setAttribute("siteURI", siteURI.spec);
} }
}
var popup = document.createElementNS(XULNS, "menupopup"); var popup = document.createElementNS(XULNS, "menupopup");
popup.setAttribute("type", "places"); popup.setAttribute("type", "places");
element.appendChild(popup); element.appendChild(popup);
@ -263,16 +247,24 @@
// If this is a child of the bookmarks menubar, we have to manually attach // If this is a child of the bookmarks menubar, we have to manually attach
// its xbl binding, because it's not a dom node and the style rules don't // its xbl binding, because it's not a dom node and the style rules don't
// get applied correctly. // get applied correctly.
if (needsBindingAttachment) { if (this._needsBindingAttachment) {
const MENU_URI = "chrome://browser/content/places/menu.xml#places-menupopup"; const MENU_URI = "chrome://browser/content/places/menu.xml#places-menupopup";
document.addBinding(popup, MENU_URI); document.addBinding(popup, MENU_URI);
} }
#endif #endif
popup._containerNodesMap = this._containerNodesMap;
this._containerNodesMap.push({ resultNode: child, domNode: popup });
} }
// else if (nodeIsQuery) ... add menu to build kids // else if (nodeIsQuery) ... add menu to build kids
if (element) { if (element) {
element.node = child; element.node = child;
if (child.icon)
element.setAttribute("image", child.icon.spec);
if (before)
this.insertBefore(element, before);
else {
// Add the new element to the menu. If there is static content at // Add the new element to the menu. If there is static content at
// the end of the menu, add the element before that. Otherwise, // the end of the menu, add the element before that. Otherwise,
// just add to the end. // just add to the end.
@ -281,27 +273,201 @@
else else
this.appendChild(element); this.appendChild(element);
} }
if (child.icon)
element.setAttribute("image", child.icon.spec);
} }
} else { ]]></body>
</method>
#ifdef XP_MACOSX
<field name="_needsBindingAttachment">false</field>
#endif
<method name="_rebuild">
<body><![CDATA[
// Make sure not to hold onto any references to menu nodes when we
// rebuild, since rebuilding deletes all the nodes in the menu and
// re-adds them. If we use a reference to a deleted node, all kinds
// of exceptions and asserts will fire.
if (this._DNDObserver._overFolder.node)
this._DNDObserver._clearOverFolder();
this._cleanMenu();
NS_ASSERT(PlacesUtils.nodeIsContainer(this._resultNode),
"Result-node of a places popup has to be a container node");
#ifdef XP_MACOSX
// On Mac OSX, we need to manually attach the XBL binding for the bookmarks menu,
// because menus in the OSX menubar aren't real DOM nodes, and they don't get styles
// applied.
var currentNode = this.parentNode;
while (currentNode && !this._needsBindingAttachment) {
if (currentNode.id && currentNode.id == "bookmarksMenu")
this._needsBindingAttachment = true;
currentNode = currentNode.parentNode;
}
#endif
var cc = this._resultNode.childCount;
if (cc > 0) {
for (var i = 0; i < cc; ++i) {
var child = this._resultNode.getChild(i);
this.insertNewItem(child, null);
}
}
else {
// This menu is empty. If there is no static content, add // This menu is empty. If there is no static content, add
// an element to show it is empty. // an element to show it is empty.
if (this._startMarker == -1 && this._endMarker == -1) { if (this._startMarker == -1 && this._endMarker == -1) {
var label = PlacesUtils.getString("bookmarksMenuEmptyFolder"); var label = PlacesUtils.getString("bookmarksMenuEmptyFolder");
var element = null; const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
element = document.createElementNS(XULNS, "menuitem"); var element = document.createElementNS(XULNS, "menuitem");
element.setAttribute("label", label); element.setAttribute("label", label);
element.setAttribute("disabled", true); element.setAttribute("disabled", true);
this.appendChild(element); this.appendChild(element);
} }
} }
// Reset the container to the same state it was in before the function was called.
this._resultNode.containerOpen = wasOpen;
]]></body> ]]></body>
</method> </method>
<!-- nsINavHistoryResultViewer -->
<field name="_viewer"><![CDATA[({
_self: this,
_forwardToChildView:
function PMV__forwardToChildView(aNode, aFunction, aArguments) {
for (var i=0; i < this._self._containerNodesMap.length; i++) {
if (this._self._containerNodesMap[i].resultNode == aNode) {
var childView = this._self._containerNodesMap[i].domNode._viewer;
childView[aFunction].apply(childView, aArguments);
return;
}
}
thorw("Container view not found");
},
itemInserted: function PMV_itemInserted(aParentNode, aNode, aIndex) {
if (aParentNode == this._self.getResultNode()) {
var index = this._self._startMarker + 1 + aIndex;
var before = this._self.childNodes[index] || null;
this._self.insertNewItem(aNode, before);
}
else
this._forwardToChildView(aParentNode, "itemInserted", arguments);
},
itemRemoved: function PMV_itemRemoved(aParentNode, aNode, aIndex) {
if (aParentNode == this._self.getResultNode()) {
var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) {
this._self.removeItem(children[i]);
return;
}
}
}
else
this._forwardToChildView(aParentNode, "itemRemoved", arguments);
},
itemChanged: function PMV_itemChanged(aNode) {
// this check can be removed once we fix bug #382397
var parentNode = aNode.parent;
if (!parentNode)
return;
if (parentNode != this._self.getResultNode()) {
this._forwardToChildView(parentNode, "itemChanged", arguments);
return;
}
var menuitem;
var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) {
menuitem = children[i];
break;
}
}
NS_ASSERT(menuitem, "unable to find menuitem");
if (!menuitem)
return;
var title = aNode.title;
if (PlacesUtils.nodeIsSeparator(aNode)) {
// nothing to do when a separator changes
return;
}
else if (PlacesUtils.nodeIsContainer(aNode)) {
if (PlacesUtils.nodeIsLivemarkContainer(aNode)) {
var folder = aNode.itemId;
var siteURIString = PlacesUtils.livemarks.getSiteURI(folder);
if (siteURIString) {
if (menuitem.getAttribute("siteURI") != siteURIString)
menuitem.setAttribute("siteURI", siteURIString);
}
else
menuitem.removeAttribute("siteURI");
}
}
if (aNode.icon) {
if (menuitem.getAttribute("image") != aNode.icon.spec)
menuitem.setAttribute("image", aNode.icon.spec);
}
else
menuitem.removeAttribute("image");
if (menuitem.getAttribute("label") != title)
menuitem.setAttribute("label", title);
},
itemReplaced:
function PMV_itemReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
if (aParentNode == this._self.getResultNode()) {
var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aOldNode) {
var next = children[i].nextSibling;
this._self.removeItem(children[i]);
this._self.insertNewItem(aNewNode, next);
return;
}
}
}
else
this._forwardToChildView(aParentNode, "itemReplaced", arguments);
},
containerOpened: function PMV_containerOpened(aNode) {
if (aNode == this._self.getResultNode())
this._self._rebuild();
else
this._forwardToChildView(aNode, "containerOpened", arguments);
},
containerClosed: function PMV_containerClosed(aNode) {
},
invalidateContainer: function PMV_invalidateContainer(aNode) {
if (aNode == this._self.getResultNode())
this._self._rebuild();
else {
this._forwardToChildView(aNode, "invalidateContainer", arguments);
}
},
invalidateAll: function PMV_invalidateAll() {
this._self._rebuild();
},
sortingChanged: function PMV_sortingChanged(aSortingMode) {
}
})]]></field>
<!-- Sometimes calling hidePopup() on a menu can leave submenus <!-- Sometimes calling hidePopup() on a menu can leave submenus
open. This calls hidePopup() on the menu and recursively open. This calls hidePopup() on the menu and recursively
hides its submenus as well. --> hides its submenus as well. -->
@ -332,9 +498,9 @@
PlacesUtils.history.executeQueries(queries.value, PlacesUtils.history.executeQueries(queries.value,
queries.value.length, queries.value.length,
options.value); options.value);
this._result.viewer = this._viewer;
this._resultNode = this._result.root; this._resultNode = this._result.root;
if (this._resultNode.containerOpen) this._containerNodesMap = [];
this._rebuild();
return val; return val;
]]></setter> ]]></setter>
</property> </property>
@ -578,7 +744,7 @@
dropPoint.folderNode = xulNode; dropPoint.folderNode = xulNode;
return dropPoint; return dropPoint;
} }
} else{ } else {
// This is a non-folder node. If the mouse is above the middle, // This is a non-folder node. If the mouse is above the middle,
// drop above the folder. Otherwise, drop below. // drop above the folder. Otherwise, drop below.
if (event.clientY < nodeY + (nodeHeight / 2)) { if (event.clientY < nodeY + (nodeHeight / 2)) {
@ -678,7 +844,6 @@
flavorSet.appendFlavour(this._self.peerDropTypes[i]); flavorSet.appendFlavour(this._self.peerDropTypes[i]);
return flavorSet; return flavorSet;
} }
})]]></field> })]]></field>
<!-- Checks whether and event should be acted on by this menu <!-- Checks whether and event should be acted on by this menu
@ -710,7 +875,7 @@
]]></body> ]]></body>
</method> </method>
<property name="selType" onget="return 'single';"/> <property name="selType" readonly="true" onget="return 'single';"/>
<method name="buildContextMenu"> <method name="buildContextMenu">
<parameter name="aPopup"/> <parameter name="aPopup"/>
@ -736,17 +901,18 @@
this.onPopupShowing(); this.onPopupShowing();
</handler> </handler>
<handler event="popuphidden"> <handler event="popuphidden">
if (event.target == this) { if (event.target != this)
if (PlacesUtils.nodeIsContainer(this._resultNode)) { return;
this._resultNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
this._resultNode.containerOpen = false; // UI performance: keep the resultnode open so we don't rebuild its
} // contents whenever the popup is reopened.
// The autoopened attribute is set for folders which have been // The autoopened attribute is set for folders which have been
// automatically opened when dragged over. Turn off this attribute // automatically opened when dragged over. Turn off this attribute
// when the folder closes because it is no longer applicable. // when the folder closes because it is no longer applicable.
this.removeAttribute("autoopened"); this.removeAttribute("autoopened");
}
</handler> </handler>
<!-- Set selected node/active view on mousedown/DOMMenuItemActive events <!-- Set selected node/active view on mousedown/DOMMenuItemActive events
so that they're set up when command and click events fire. --> so that they're set up when command and click events fire. -->
<handler event="mousedown"><![CDATA[ <handler event="mousedown"><![CDATA[

Просмотреть файл

@ -213,15 +213,13 @@
// No context menus on menuitems on Mac // No context menus on menuitems on Mac
popup.setAttribute("context", "placesContext"); popup.setAttribute("context", "placesContext");
#endif #endif
popup._result = this._result; popup.place = this.place;
popup._resultNode = this._result.root;
var t = this; var t = this;
popup.popupShowingCallback = function() {t.chevronPopupShowing();}; popup.popupShowingCallback = function() { t.chevronPopupShowing(); };
// This needs to be in a timeout to make sure our boxObject has time // This needs to be in a timeout to make sure our boxObject has time
// to get its proper size // to get its proper size
var self = this; setTimeout(function() { t.updateChevron(); }, 0);
setTimeout(function() { self.updateChevron(); }, 0);
]]></body> ]]></body>
</method> </method>
@ -274,6 +272,9 @@
button.appendChild(popup); button.appendChild(popup);
popup._result = this._result; popup._result = this._result;
popup._resultNode = child; popup._resultNode = child;
popup._containerNodesMap = this._containerNodesMap;
this._containerNodesMap.push({ resultNode: child,
domNode: popup });
} }
button.setAttribute("label", title); button.setAttribute("label", title);
@ -289,24 +290,24 @@
</method> </method>
<method name="removeItem"> <method name="removeItem">
<parameter name="parent"/>
<parameter name="child"/> <parameter name="child"/>
<body><![CDATA[ <body><![CDATA[
if (PlacesUtils.nodeIsFolder(parent) && if (PlacesUtils.nodeIsContainer(child.node)) {
parent.itemId == PlacesUtils.bookmarks.toolbarFolder) for (var i=0; i < this._containerNodesMap.length; i++) {
return this.removeChild(child); if (this._containerNodesMap[i].resultNode == child.node)
return null; this._containerNodesMap.splice(i, 1);
}
}
this.removeChild(child);
]]></body> ]]></body>
</method> </method>
<method name="chevronPopupShowing"> <method name="chevronPopupShowing">
<body><![CDATA[ <body><![CDATA[
var popup = this._chevron.firstChild; var popup = this._chevron.firstChild;
for (var i = 0; i < popup.childNodes.length; i++) { for (var i = 0; i < popup.childNodes.length; i++)
if (!this.childNodes[i].collapsed) { popup.childNodes[i].hidden = !this.childNodes[i].collapsed;
popup.childNodes[i].hidden = true;
}
}
]]></body> ]]></body>
</method> </method>
@ -370,7 +371,6 @@
options.value); options.value);
this._result.viewer = this._viewer; this._result.viewer = this._viewer;
this._result.root.containerOpen = true; this._result.root.containerOpen = true;
this._rebuild();
} }
catch(ex) { catch(ex) {
// Invalid query, or had no results. // Invalid query, or had no results.
@ -474,40 +474,66 @@
]]></body> ]]></body>
</method> </method>
<!-- Map for containerNodes<->domNodes. There's only one map per
result/viewer, i.e. the field is initialized just for the toolbar,
for sub menus it is set to the root's map -->
<field name="_containerNodesMap">[]</field>
<!-- nsINavHistoryResultViewer --> <!-- nsINavHistoryResultViewer -->
<field name="_viewer"><![CDATA[({ <field name="_viewer"><![CDATA[({
_self: this, _self: this,
itemInserted: function MV_V_itemInserted(aParentNode, aNode, aIndex) {
_forwardToChildView:
function TV_V__forwardToChildView(aNode, aFunction, aArguments) {
for (var i=0; i < this._self._containerNodesMap.length; i++) {
if (this._self._containerNodesMap[i].resultNode == aNode) {
var childView = this._self._containerNodesMap[i].domNode._viewer;
childView[aFunction].apply(childView, aArguments);
return;
}
}
thorw("Container view not found");
},
itemInserted: function TV_V_itemInserted(aParentNode, aNode, aIndex) {
// don't insert new items into the toolbar // don't insert new items into the toolbar
// if the parent is not the root // if the parent is not the root
if (PlacesUtils.nodeIsFolder(aParentNode) && if (aParentNode == this._self.getResult().root) {
aParentNode == this._self.getResult().root) {
var children = this._self.childNodes; var children = this._self.childNodes;
this._self.insertNewItem(aNode, this._self.insertNewItem(aNode,
aIndex < children.length ? children[aIndex] : null); aIndex < children.length ? children[aIndex] : null);
this._self.updateChevron(); this._self.updateChevron();
} }
else {
this._forwardToChildView(aParentNode, "itemInserted", arguments);
}
}, },
itemRemoved: function MV_V_itemRemoved(aParentNode, aNode, aIndex) {
itemRemoved: function TV_V_itemRemoved(aParentNode, aNode, aIndex) {
if (aParentNode == this._self.getResult().root) {
var children = this._self.childNodes; var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) { if (children[i].node == aNode) {
this._self.removeItem(aParentNode, children[i]); this._self.removeItem(children[i]);
this._self.updateChevron(); this._self.updateChevron();
return; return;
} }
} }
}
else
this._forwardToChildView(aParentNode, "itemRemoved", arguments);
}, },
itemChanged: function MV_V_itemChanged(aNode) {
itemChanged: function TV_V_itemChanged(aNode) {
// this check can be removed once we fix bug #382397 // this check can be removed once we fix bug #382397
if (!aNode.parent) var parentNode = aNode.parent;
if (!parentNode)
return; return;
// for the toolbar, if (parentNode != this._self.getResult().root) {
// we only care if children of the root are changing this._forwardToChildView(parentNode, "itemChanged", arguments);
if (!PlacesUtils.nodeIsFolder(aNode.parent) ||
aNode.parent != this._self.getResult().root)
return; return;
}
var button; var button;
@ -556,35 +582,52 @@
// the only change that might require a chevron update // the only change that might require a chevron update
// is when the title changes // is when the title changes
if (button.getAttribute("label") != title) if (button.getAttribute("label") != title) {
{
button.setAttribute("label", title); button.setAttribute("label", title);
this._self.updateChevron(); this._self.updateChevron();
} }
}, },
itemReplaced: function MV_V_itemReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
itemReplaced:
function TV_V_itemReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
if (aParentNode == this._self.getResult().root) {
var children = this._self.childNodes; var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
if (children[i].node == aOldNode) { if (children[i].node == aOldNode) {
var next = children[i].nextSibling; var next = children[i].nextSibling;
if (this._self.removeItem(aParentNode, children[i])) { this._self.removeItem(children[i]);
this._self.insertNewItem(aNewNode, next); this._self.insertNewItem(aNewNode, next);
this._self.updateChevron(); this._self.updateChevron();
}
return; return;
} }
} }
}
else
this._forwardToChildView(aParentNode, "itemReplaced", arguments);
}, },
containerOpened: function MV_V_containerOpened(aNode) {
containerOpened: function TV_V_containerOpened(aNode) {
if (aNode == this._self.getResult().root)
this._self._rebuild();
else
this._forwardToChildView(aNode, "containerOpened", arguments);
}, },
containerClosed: function MV_V_containerClosed(aNode) {
containerClosed: function TV_V_containerClosed(aNode) {
}, },
invalidateContainer: function MV_V_invalidateContainer(aNode) {
invalidateContainer: function TV_V_invalidateContainer(aNode) {
if (aNode == this._self.getResult().root)
this._self._rebuild();
else
this._forwardToChildView(aNode, "invalidateContainer", arguments);
}, },
invalidateAll: function MV_V_invalidateAll() {
invalidateAll: function TV_V_invalidateAll() {
this._self._rebuild(); this._self._rebuild();
}, },
sortingChanged: function MV_V_sortingChanged(aSortingMode) {
sortingChanged: function TV_V_sortingChanged(aSortingMode) {
} }
})]]></field> })]]></field>