Changeset 35317 in webkit


Ignore:
Timestamp:
Jul 23, 2008 7:48:36 PM (16 years ago)
Author:
timothy@apple.com
Message:

Updates the elements DOM tree when nodes are added or removed from
the inspected document.

https://bugs.webkit.org/show_bug.cgi?id=6590
<rdar://problem/5712921>

Reviewed by Adam Roben.

  • loader/FrameLoader.cpp: (WebCore::FrameLoader::dispatchWindowObjectAvailable): Added a call to InspectorController::inspectedWindowScriptObjectCleared.
  • page/InspectorController.cpp: (WebCore::InspectorController::inspectedWindowScriptObjectCleared): Calls the WebInspector.inspectedWindowCleared script function.
  • page/InspectorController.h:
  • page/inspector/ElementsPanel.js: (WebInspector.ElementsPanel): Create the event listener callback wrappers. (WebInspector.ElementsPanel.prototype.show): Call _updateModifiedNodes if there are any recently modified nodes. (WebInspector.ElementsPanel.prototype.reset): Remove previous mutation event listeners. Adds a check for InspectorController.isWindowVisible to prevent adding event listeners when the window isn't visible. (WebInspector.ElementsPanel.prototype.inspectedWindowCleared): (WebInspector.ElementsPanel.prototype._addMutationEventListeners): Add DOMNodeInserted, DOMNodeRemoved and DOMContentLoaded event listeners to the passed in window or window's document. (WebInspector.ElementsPanel.prototype._removeMutationEventListeners): Removes the event listeners added in _addMutationEventListeners. (WebInspector.ElementsPanel.prototype.updateMutationEventListeners): Call _addMutationEventListeners again to reinstate the listners if the document changed or window cleared them. (WebInspector.ElementsPanel.prototype.registerMutationEventListeners): Append the window to _mutationMonitoredWindows and call _addMutationEventListeners. (WebInspector.ElementsPanel.prototype.unregisterMutationEventListeners): Remove the window from _mutationMonitoredWindows and call _removeMutationEventListeners. (WebInspector.ElementsPanel.prototype.unregisterAllMutationEventListeners): Call _removeMutationEventListeners for all windows in _mutationMonitoredWindows and clear _mutationMonitoredWindows. (WebInspector.ElementsPanel.prototype._contentLoaded): Append the node and parent to the recentlyModifiedNodes array. Call _updateModifiedNodesSoon if visible. (WebInspector.ElementsPanel.prototype._nodeInserted): Ditto. (WebInspector.ElementsPanel.prototype._nodeRemoved): Ditto. (WebInspector.ElementsPanel.prototype._updateModifiedNodesSoon): Call _updateModifiedNodes on a zero timeout. (WebInspector.ElementsPanel.prototype._updateModifiedNodes): Iterate over the recentlyModifiedNodes array and call updateChildren on all the parent elements that had changes. Only calls updateChildren once per parent element. (WebInspector.ElementsPanel.prototype._isAncestorIncludingParentFrames): Return false if the nodes are the same. Return true if the nodes are the same while looking at ancestor frame elements. THis use to return false, which was incorrect. (WebInspector.DOMNodeTreeElement.prototype.onpopulate): Call updateChildren. (WebInspector.DOMNodeTreeElement.prototype.updateChildren): Copied from onpopulate and changed to rebuild the children elements by adding new children, moving existing children and removed old children. (WebInspector.DOMNodeTreeElement.prototype.onexpand): If the node has a contentDocument call registerMutationEventListeners to track any mutations.
  • page/inspector/inspector.js: (WebInspector.inspectedWindowCleared): Call ElementsPanel.inspectedWindowCleared.
  • page/inspector/treeoutline.js: (TreeElement.prototype.get hasChildren): Return _hasChildren. (TreeElement.prototype.set hasChildren): Set _hasChildren and update the className. (TreeElement.prototype.hasAncestor): Return true if the element has the passed in ancestor. (TreeElement.prototype.expand): Fix an exception that can happen if expand is called before _attach.
  • WebCore/manual-tests/inspector/dom-mutation.html: Added.
  • WebCore/manual-tests/inspector/resources/mutate-frame-2.html: Added.
  • WebCore/manual-tests/inspector/resources/mutate-frame.html: Added.
Location:
trunk/WebCore
Files:
3 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r35316 r35317  
     12008-07-23  Timothy Hatcher  <timothy@apple.com>
     2
     3        Updates the elements DOM tree when nodes are added or removed from
     4        the inspected document.
     5
     6        https://bugs.webkit.org/show_bug.cgi?id=6590
     7        <rdar://problem/5712921>
     8
     9        Reviewed by Adam Roben.
     10
     11        * loader/FrameLoader.cpp:
     12        (WebCore::FrameLoader::dispatchWindowObjectAvailable): Added a call to
     13        InspectorController::inspectedWindowScriptObjectCleared.
     14        * page/InspectorController.cpp:
     15        (WebCore::InspectorController::inspectedWindowScriptObjectCleared):
     16        Calls the WebInspector.inspectedWindowCleared script function.
     17        * page/InspectorController.h:
     18        * page/inspector/ElementsPanel.js:
     19        (WebInspector.ElementsPanel): Create the event listener callback wrappers.
     20        (WebInspector.ElementsPanel.prototype.show): Call _updateModifiedNodes if
     21        there are any recently modified nodes.
     22        (WebInspector.ElementsPanel.prototype.reset): Remove previous mutation event listeners.
     23        Adds a check for InspectorController.isWindowVisible to prevent adding
     24        event listeners when the window isn't visible.
     25        (WebInspector.ElementsPanel.prototype.inspectedWindowCleared):
     26        (WebInspector.ElementsPanel.prototype._addMutationEventListeners): Add DOMNodeInserted,
     27        DOMNodeRemoved and DOMContentLoaded event listeners to the passed in window or window's document.
     28        (WebInspector.ElementsPanel.prototype._removeMutationEventListeners): Removes the event listeners
     29        added in _addMutationEventListeners.
     30        (WebInspector.ElementsPanel.prototype.updateMutationEventListeners): Call _addMutationEventListeners
     31        again to reinstate the listners if the document changed or window cleared them.
     32        (WebInspector.ElementsPanel.prototype.registerMutationEventListeners): Append the window to
     33        _mutationMonitoredWindows and call _addMutationEventListeners.
     34        (WebInspector.ElementsPanel.prototype.unregisterMutationEventListeners): Remove the window from
     35        _mutationMonitoredWindows and call _removeMutationEventListeners.
     36        (WebInspector.ElementsPanel.prototype.unregisterAllMutationEventListeners): Call
     37        _removeMutationEventListeners for all windows in _mutationMonitoredWindows and
     38        clear _mutationMonitoredWindows.
     39        (WebInspector.ElementsPanel.prototype._contentLoaded): Append the node and parent
     40        to the recentlyModifiedNodes array. Call _updateModifiedNodesSoon if visible.
     41        (WebInspector.ElementsPanel.prototype._nodeInserted): Ditto.
     42        (WebInspector.ElementsPanel.prototype._nodeRemoved): Ditto.
     43        (WebInspector.ElementsPanel.prototype._updateModifiedNodesSoon): Call
     44        _updateModifiedNodes on a zero timeout.
     45        (WebInspector.ElementsPanel.prototype._updateModifiedNodes): Iterate over
     46        the recentlyModifiedNodes array and call updateChildren on all the parent
     47        elements that had changes. Only calls updateChildren once per parent element.
     48        (WebInspector.ElementsPanel.prototype._isAncestorIncludingParentFrames): Return
     49        false if the nodes are the same. Return true if the nodes are the same while
     50        looking at ancestor frame elements. THis use to return false, which was incorrect.
     51        (WebInspector.DOMNodeTreeElement.prototype.onpopulate): Call updateChildren.
     52        (WebInspector.DOMNodeTreeElement.prototype.updateChildren): Copied from
     53        onpopulate and changed to rebuild the children elements by adding new children,
     54        moving existing children and removed old children.
     55        (WebInspector.DOMNodeTreeElement.prototype.onexpand): If the node has a contentDocument
     56        call registerMutationEventListeners to track any mutations.
     57        * page/inspector/inspector.js:
     58        (WebInspector.inspectedWindowCleared): Call ElementsPanel.inspectedWindowCleared.
     59        * page/inspector/treeoutline.js:
     60        (TreeElement.prototype.get hasChildren): Return _hasChildren.
     61        (TreeElement.prototype.set hasChildren): Set _hasChildren and update the className.
     62        (TreeElement.prototype.hasAncestor): Return true if the element has the passed in ancestor.
     63        (TreeElement.prototype.expand): Fix an exception that can happen if expand is
     64        called before _attach.
     65        * WebCore/manual-tests/inspector/dom-mutation.html: Added.
     66        * WebCore/manual-tests/inspector/resources/mutate-frame-2.html: Added.
     67        * WebCore/manual-tests/inspector/resources/mutate-frame.html: Added.
     68
    1692008-07-22  Timothy Hatcher  <timothy@apple.com>
    270
  • trunk/WebCore/loader/FrameLoader.cpp

    r35298 r35317  
    47854785
    47864786    m_client->windowObjectCleared();
    4787     if (Page* page = m_frame->page())
     4787
     4788    if (Page* page = m_frame->page()) {
     4789        if (InspectorController* inspector = page->inspectorController())
     4790            inspector->inspectedWindowScriptObjectCleared(m_frame);
    47884791        if (InspectorController* inspector = page->parentInspectorController())
    47894792            inspector->windowScriptObjectAvailable();
     4793    }
    47904794}
    47914795
  • trunk/WebCore/page/InspectorController.cpp

    r35314 r35317  
    12441244}
    12451245
     1246void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
     1247{
     1248    if (!enabled() || !m_scriptContext || !m_scriptObject)
     1249        return;
     1250
     1251    JSDOMWindow* win = toJSDOMWindow(frame);
     1252    ExecState* exec = win->globalExec();
     1253
     1254    JSValueRef arg0;
     1255
     1256    {
     1257        KJS::JSLock lock(false);
     1258        arg0 = toRef(JSInspectedObjectWrapper::wrap(exec, win));
     1259    }
     1260
     1261    JSValueRef exception = 0;
     1262    callFunction(m_scriptContext, m_scriptObject, "inspectedWindowCleared", 1, &arg0, exception);
     1263}
     1264
    12461265void InspectorController::windowScriptObjectAvailable()
    12471266{
  • trunk/WebCore/page/InspectorController.h

    r34696 r35317  
    121121    void setScriptContext(JSContextRef context) { m_scriptContext = context; };
    122122
     123    void inspectedWindowScriptObjectCleared(Frame*);
    123124    void windowScriptObjectAvailable();
    124125
  • trunk/WebCore/page/inspector/ElementsPanel.js

    r35316 r35317  
    7777    this.element.appendChild(this.sidebarResizeElement);
    7878
     79    this._mutationMonitoredWindows = [];
     80    this._nodeInsertedEventListener = InspectorController.wrapCallback(this._nodeInserted.bind(this));
     81    this._nodeRemovedEventListener = InspectorController.wrapCallback(this._nodeRemoved.bind(this));
     82    this._contentLoadedEventListener = InspectorController.wrapCallback(this._contentLoaded.bind(this));
     83
    7984    this.reset();
    8085}
     
    104109        this.updateBreadcrumb();
    105110        this.updateTreeSelection();
     111        if (this.recentlyModifiedNodes.length)
     112            this._updateModifiedNodes();
    106113    },
    107114
     
    129136        this.forceHoverHighlight = false;
    130137
     138        this.recentlyModifiedNodes = [];
     139        this.unregisterAllMutationEventListeners();
     140
    131141        var inspectedWindow = InspectorController.inspectedWindow();
    132142        if (!inspectedWindow || !inspectedWindow.document)
     
    142152
    143153            var contentLoadedCallback = InspectorController.wrapCallback(contentLoaded.bind(this));
    144 
    145154            inspectedWindow.document.addEventListener("DOMContentLoaded", contentLoadedCallback, false);
    146155            return;
    147156        }
     157
     158        // If the window isn't visible, return early so the DOM tree isn't built
     159        // and mutation event listeners are not added.
     160        if (!InspectorController.isWindowVisible())
     161            return;
     162
     163        this.registerMutationEventListeners(inspectedWindow);
    148164
    149165        var inspectedRootDocument = inspectedWindow.document;
     
    158174    },
    159175
     176    inspectedWindowCleared: function(window)
     177    {
     178        if (InspectorController.isWindowVisible())
     179            this.updateMutationEventListeners(window);
     180    },
     181
     182    _addMutationEventListeners: function(monitoredWindow)
     183    {
     184        monitoredWindow.document.addEventListener("DOMNodeInserted", this._nodeInsertedEventListener, true);
     185        monitoredWindow.document.addEventListener("DOMNodeRemoved", this._nodeRemovedEventListener, true);
     186        if (monitoredWindow.frameElement)
     187            monitoredWindow.addEventListener("DOMContentLoaded", this._contentLoadedEventListener, true);
     188    },
     189
     190    _removeMutationEventListeners: function(monitoredWindow)
     191    {
     192        if (monitoredWindow.frameElement)
     193            monitoredWindow.removeEventListener("DOMContentLoaded", this._contentLoadedEventListener, true);
     194        if (!monitoredWindow.document)
     195            return;
     196        monitoredWindow.document.removeEventListener("DOMNodeInserted", this._nodeInsertedEventListener, true);
     197        monitoredWindow.document.removeEventListener("DOMNodeRemoved", this._nodeRemovedEventListener, true);
     198    },
     199
     200    updateMutationEventListeners: function(monitoredWindow)
     201    {
     202        this._addMutationEventListeners(monitoredWindow);
     203    },
     204
     205    registerMutationEventListeners: function(monitoredWindow)
     206    {
     207        if (!monitoredWindow || this._mutationMonitoredWindows.indexOf(monitoredWindow) !== -1)
     208            return;
     209        this._mutationMonitoredWindows.push(monitoredWindow);
     210        if (InspectorController.isWindowVisible())
     211            this._addMutationEventListeners(monitoredWindow);
     212    },
     213
     214    unregisterMutationEventListeners: function(monitoredWindow)
     215    {
     216        if (!monitoredWindow || this._mutationMonitoredWindows.indexOf(monitoredWindow) === -1)
     217            return;
     218        this._mutationMonitoredWindows.remove(monitoredWindow);
     219        this._removeMutationEventListeners(monitoredWindow);
     220    },
     221
     222    unregisterAllMutationEventListeners: function()
     223    {
     224        for (var i = 0; i < this._mutationMonitoredWindows.length; ++i)
     225            this._removeMutationEventListeners(this._mutationMonitoredWindows[i]);
     226        this._mutationMonitoredWindows = [];
     227    },
     228
    160229    updateTreeSelection: function()
    161230    {
     
    257326        else
    258327            this._updateHoverHighlight();
     328    },
     329
     330    _contentLoaded: function(event)
     331    {
     332        this.recentlyModifiedNodes.push({node: event.target, parent: event.target.defaultView.frameElement, replaced: true});
     333        if (this.visible)
     334            this._updateModifiedNodesSoon();
     335    },
     336
     337    _nodeInserted: function(event)
     338    {
     339        this.recentlyModifiedNodes.push({node: event.target, parent: event.relatedNode, inserted: true});
     340        if (this.visible)
     341            this._updateModifiedNodesSoon();
     342    },
     343
     344    _nodeRemoved: function(event)
     345    {
     346        this.recentlyModifiedNodes.push({node: event.target, parent: event.relatedNode, removed: true});
     347        if (this.visible)
     348            this._updateModifiedNodesSoon();
     349    },
     350
     351    _updateModifiedNodesSoon: function()
     352    {
     353        if ("_updateModifiedNodesTimeout" in this)
     354            return;
     355        this._updateModifiedNodesTimeout = setTimeout(this._updateModifiedNodes.bind(this), 0);
     356    },
     357
     358    _updateModifiedNodes: function()
     359    {
     360        if ("_updateModifiedNodesTimeout" in this) {
     361            clearTimeout(this._updateModifiedNodesTimeout);
     362            delete this._updateModifiedNodesTimeout;
     363        }
     364
     365        var updatedParentTreeElements = [];
     366        var updateBreadcrumbs = false;
     367
     368        for (var i = 0; i < this.recentlyModifiedNodes.length; ++i) {
     369            var replaced = this.recentlyModifiedNodes[i].replaced;
     370            var parent = this.recentlyModifiedNodes[i].parent;
     371            if (!parent)
     372                continue;
     373
     374            var parentNodeItem = this.treeOutline.findTreeElement(parent, null, null, objectsAreSame);
     375            if (parentNodeItem && !parentNodeItem.alreadyUpdatedChildren) {
     376                parentNodeItem.updateChildren(replaced);
     377                parentNodeItem.alreadyUpdatedChildren = true;
     378                updatedParentTreeElements.push(parentNodeItem);
     379            }
     380
     381            if (!updateBreadcrumbs && (objectsAreSame(this.focusedDOMNode, parent) || this._isAncestorIncludingParentFrames(this.focusedDOMNode, parent)))
     382                updateBreadcrumbs = true;
     383        }
     384
     385        for (var i = 0; i < updatedParentTreeElements.length; ++i)
     386            delete updatedParentTreeElements[i].alreadyUpdatedChildren;
     387
     388        this.recentlyModifiedNodes = [];
     389
     390        if (updateBreadcrumbs)
     391            this.updateBreadcrumb(true);
    259392    },
    260393
     
    8891022    _isAncestorIncludingParentFrames: function(a, b)
    8901023    {
     1024        if (objectsAreSame(a, b))
     1025            return false;
    8911026        for (var node = b; node; node = this._getDocumentForNode(node).defaultView.frameElement)
    892             if (isAncestorNode.call(a, node))
     1027            if (objectsAreSame(a, node) || isAncestorNode.call(a, node))
    8931028                return true;
    8941029        return false;
     
    10381173            return;
    10391174
    1040         this.removeChildren();
    10411175        this.whitespaceIgnored = Preferences.ignoreWhitespace;
    10421176
     1177        this.updateChildren();
     1178    },
     1179
     1180    updateChildren: function(fullRefresh)
     1181    {
     1182        if (fullRefresh) {
     1183            var selectedTreeElement = this.treeOutline.selectedTreeElement;
     1184            if (selectedTreeElement && selectedTreeElement.hasAncestor(this))
     1185                this.select();
     1186            this.removeChildren();
     1187        }
     1188
    10431189        var treeElement = this;
    1044         function appendChildrenOfNode(node)
     1190        var treeChildIndex = 0;
     1191
     1192        function updateChildrenOfNode(node)
    10451193        {
     1194            var treeOutline = treeElement.treeOutline;
    10461195            var child = (Preferences.ignoreWhitespace ? firstChildSkippingWhitespace.call(node) : node.firstChild);
    10471196            while (child) {
    1048                 treeElement.appendChild(new WebInspector.DOMNodeTreeElement(child));
     1197                var currentTreeElement = treeElement.children[treeChildIndex];
     1198                if (!currentTreeElement || !objectsAreSame(currentTreeElement.representedObject, child)) {
     1199                    // Find any existing element that is later in the children list.
     1200                    var existingTreeElement = null;
     1201                    for (var i = (treeChildIndex + 1); i < treeElement.children.length; ++i) {
     1202                        if (objectsAreSame(treeElement.children[i].representedObject, child)) {
     1203                            existingTreeElement = treeElement.children[i];
     1204                            break;
     1205                        }
     1206                    }
     1207
     1208                    if (existingTreeElement && existingTreeElement.parent === treeElement) {
     1209                        // If an existing element was found and it has the same parent, just move it.
     1210                        var wasSelected = existingTreeElement.selected;
     1211                        treeElement.removeChild(existingTreeElement);
     1212                        treeElement.insertChild(existingTreeElement, treeChildIndex);
     1213                        if (wasSelected)
     1214                            existingTreeElement.select();
     1215                    } else {
     1216                        // No existing element found, insert a new element.
     1217                        treeElement.insertChild(new WebInspector.DOMNodeTreeElement(child), treeChildIndex);
     1218                    }
     1219                }
     1220
    10491221                child = Preferences.ignoreWhitespace ? nextSiblingSkippingWhitespace.call(child) : child.nextSibling;
    1050             }
     1222                ++treeChildIndex;
     1223            }
     1224        }
     1225
     1226        // Remove any tree elements that no longer have this node (or this node's contentDocument) as their parent.
     1227        for (var i = (this.children.length - 1); i >= 0; --i) {
     1228            if ("elementCloseTag" in this.children[i])
     1229                continue;
     1230
     1231            var currentChild = this.children[i];
     1232            var currentNode = currentChild.representedObject;
     1233            var currentParentNode = currentNode.parentNode;
     1234
     1235            if (objectsAreSame(currentParentNode, this.representedObject))
     1236                continue;
     1237            if (this.representedObject.contentDocument && objectsAreSame(currentParentNode, this.representedObject.contentDocument))
     1238                continue;
     1239
     1240            var selectedTreeElement = this.treeOutline.selectedTreeElement;
     1241            if (selectedTreeElement && (selectedTreeElement === currentChild || selectedTreeElement.hasAncestor(currentChild)))
     1242                this.select();
     1243
     1244            this.removeChildAtIndex(i);
     1245
     1246            if (currentNode.contentDocument)
     1247                this.treeOutline.panel.unregisterMutationEventListeners(currentNode.contentDocument.defaultView);
    10511248        }
    10521249
    10531250        if (this.representedObject.contentDocument)
    1054             appendChildrenOfNode(this.representedObject.contentDocument);
    1055 
    1056         appendChildrenOfNode(this.representedObject);
    1057 
    1058         if (this.representedObject.nodeType == Node.ELEMENT_NODE) {
     1251            updateChildrenOfNode(this.representedObject.contentDocument);
     1252        updateChildrenOfNode(this.representedObject);
     1253
     1254        var lastChild = this.children[this.children.length - 1];
     1255        if (this.representedObject.nodeType == Node.ELEMENT_NODE && (!lastChild || !lastChild.elementCloseTag)) {
    10591256            var title = "<span class=\"webkit-html-tag close\">&lt;/" + this.representedObject.nodeName.toLowerCase().escapeHTML() + "&gt;</span>";
    1060             var item = new TreeElement(title, this.representedObject, false);
     1257            var item = new TreeElement(title, null, false);
    10611258            item.selectable = false;
     1259            item.elementCloseTag = true;
    10621260            this.appendChild(item);
    10631261        }
     
    10671265    {
    10681266        this.treeOutline.panel.updateTreeSelection();
     1267
     1268        if (this.representedObject.contentDocument)
     1269            this.treeOutline.panel.registerMutationEventListeners(this.representedObject.contentDocument.defaultView);
    10691270    },
    10701271
  • trunk/WebCore/page/inspector/inspector.js

    r35113 r35317  
    785785
    786786    this.console.clearMessages();
     787}
     788
     789WebInspector.inspectedWindowCleared = function(inspectedWindow)
     790{
     791    this.panels.elements.inspectedWindowCleared(inspectedWindow);
    787792}
    788793
  • trunk/WebCore/page/inspector/treeoutline.js

    r35315 r35317  
    467467    },
    468468
     469    get hasChildren() {
     470        return this._hasChildren;
     471    },
     472
     473    set hasChildren(x) {
     474        if (this._hasChildren === x)
     475            return;
     476
     477        this._hasChildren = x;
     478
     479        if (!this._listItemNode)
     480            return;
     481
     482        if (x)
     483            this._listItemNode.addStyleClass("parent");
     484        else {
     485            this._listItemNode.removeStyleClass("parent");
     486            this.collapse();
     487        }
     488    },
     489
    469490    get hidden() {
    470491        return this._hidden;
     
    629650        return;
    630651
    631     if (!this._childrenListNode || this._shouldRefreshChildren) {
     652    if (this.treeOutline && (!this._childrenListNode || this._shouldRefreshChildren)) {
    632653        if (this._childrenListNode && this._childrenListNode.parentNode)
    633654            this._childrenListNode.parentNode.removeChild(this._childrenListNode);
     
    651672    if (this._listItemNode) {
    652673        this._listItemNode.addStyleClass("expanded");
    653         if (this._childrenListNode.parentNode != this._listItemNode.parentNode)
     674        if (this._childrenListNode && this._childrenListNode.parentNode != this._listItemNode.parentNode)
    654675            this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling);
    655676    }
     
    686707}
    687708
     709TreeElement.prototype.hasAncestor = function(ancestor) {
     710    if (!ancestor)
     711        return false;
     712
     713    var currentNode = this.parent;
     714    while (currentNode) {
     715        if (ancestor === currentNode)
     716            return true;
     717        currentNode = currentNode.parent;
     718    }
     719
     720    return false;
     721}
     722
    688723TreeElement.prototype.reveal = function()
    689724{
Note: See TracChangeset for help on using the changeset viewer.