Changeset 56683 in webkit


Ignore:
Timestamp:
Mar 28, 2010 1:49:39 AM (14 years ago)
Author:
Joseph Pecoraro
Message:

2010-03-27 Joseph Pecoraro <Joseph Pecoraro>

Reviewed by Pavel Feldman.

Web Inspector: Edit Tag Names
https://bugs.webkit.org/show_bug.cgi?id=36481

Allow editing an Element's Tag Name by double clicking
on the tag name in the Element's Tree Hierarchy.

The usual asynchronous InspectorBackend, InspectorDOMAgent, InspectorFrontend flow.

  • inspector/InspectorBackend.cpp: moved DOM manipulation to InspectorDOMAgent (WebCore::InspectorBackend::removeNode): (WebCore::InspectorBackend::changeTagName):
  • inspector/InspectorBackend.h:
  • inspector/InspectorBackend.idl:
  • inspector/InspectorDOMAgent.cpp: (WebCore::InspectorDOMAgent::removeNode): (WebCore::InspectorDOMAgent::changeTagName):
  • inspector/InspectorDOMAgent.h:
  • inspector/InspectorFrontend.cpp: (WebCore::InspectorFrontend::didChangeTagName):
  • inspector/InspectorFrontend.h:
  • inspector/front-end/DOMAgent.js:

Handle the UI for editing an Element's tag name.

  • inspector/front-end/ElementsTreeOutline.js: (WebInspector.ElementsTreeElement.prototype._startEditingFromEvent): allow editing from double click. (WebInspector.ElementsTreeElement.prototype._startEditingTagName.keyupListener): update the closing tag (WebInspector.ElementsTreeElement.prototype._startEditingTagName.editingComitted): remove extra listener and commit (WebInspector.ElementsTreeElement.prototype._startEditingTagName.editingCancelled): remove extra listener and cancel (WebInspector.ElementsTreeElement.prototype._startEditingTagName): (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted.cancel): (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted.moveToNextAttributeIfNeeded): (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted.editTagNameCallback): (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted): (WebInspector.ElementsTreeElement.prototype._distinctClosingTagElement): get the closing tag for an opening tag (WebInspector.ElementsTreeElement.prototype._nodeTitleInfo): wrap the tagName in span.webkit-html-tag-name

Miscellaneous updates.

  • inspector/front-end/treeoutline.js: fixed a typo.
Location:
trunk/WebCore
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r56676 r56683  
     12010-03-27  Joseph Pecoraro  <joepeck@webkit.org>
     2
     3        Reviewed by Pavel Feldman.
     4
     5        Web Inspector: Edit Tag Names
     6        https://bugs.webkit.org/show_bug.cgi?id=36481
     7       
     8        Allow editing an Element's Tag Name by double clicking
     9        on the tag name in the Element's Tree Hierarchy.
     10       
     11          The usual asynchronous InspectorBackend, InspectorDOMAgent, InspectorFrontend flow.
     12
     13        * inspector/InspectorBackend.cpp: moved DOM manipulation to InspectorDOMAgent
     14        (WebCore::InspectorBackend::removeNode):
     15        (WebCore::InspectorBackend::changeTagName):
     16        * inspector/InspectorBackend.h:
     17        * inspector/InspectorBackend.idl:
     18        * inspector/InspectorDOMAgent.cpp:
     19        (WebCore::InspectorDOMAgent::removeNode):
     20        (WebCore::InspectorDOMAgent::changeTagName):
     21        * inspector/InspectorDOMAgent.h:
     22        * inspector/InspectorFrontend.cpp:
     23        (WebCore::InspectorFrontend::didChangeTagName):
     24        * inspector/InspectorFrontend.h:
     25
     26        * inspector/front-end/DOMAgent.js:
     27
     28          Handle the UI for editing an Element's tag name.
     29
     30        * inspector/front-end/ElementsTreeOutline.js:
     31        (WebInspector.ElementsTreeElement.prototype._startEditingFromEvent): allow editing from double click.
     32        (WebInspector.ElementsTreeElement.prototype._startEditingTagName.keyupListener): update the closing tag
     33        (WebInspector.ElementsTreeElement.prototype._startEditingTagName.editingComitted): remove extra listener and commit
     34        (WebInspector.ElementsTreeElement.prototype._startEditingTagName.editingCancelled): remove extra listener and cancel
     35        (WebInspector.ElementsTreeElement.prototype._startEditingTagName):
     36        (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted.cancel):
     37        (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted.moveToNextAttributeIfNeeded):
     38        (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted.editTagNameCallback):
     39        (WebInspector.ElementsTreeElement.prototype._tagNameEditingCommitted):
     40        (WebInspector.ElementsTreeElement.prototype._distinctClosingTagElement): get the closing tag for an opening tag
     41        (WebInspector.ElementsTreeElement.prototype._nodeTitleInfo): wrap the tagName in span.webkit-html-tag-name
     42
     43          Miscellaneous updates.
     44
     45        * inspector/front-end/treeoutline.js: fixed a typo.
     46
    1472010-03-27  Dmitry Gorbik  <socket.h@gmail.com>
    248
  • trunk/WebCore/inspector/InspectorBackend.cpp

    r56107 r56683  
    327327    Pasteboard::generalPasteboard()->writePlainText(markup);
    328328}
    329    
     329
    330330void InspectorBackend::removeNode(long callId, long nodeId)
    331331{
    332     InspectorFrontend* frontend = inspectorFrontend();
    333     if (!frontend)
    334         return;
    335 
    336     Node* node = nodeForId(nodeId);
    337     if (!node) {
    338         // Use -1 to denote an error condition.
    339         frontend->didRemoveNode(callId, -1);
    340         return;
    341     }
    342 
    343     Node* parentNode = node->parentNode();
    344     if (!parentNode) {
    345         frontend->didRemoveNode(callId, -1);
    346         return;
    347     }
    348 
    349     ExceptionCode code;
    350     parentNode->removeChild(node, code);
    351     if (code) {
    352         frontend->didRemoveNode(callId, -1);
    353         return;
    354     }
    355 
    356     frontend->didRemoveNode(callId, nodeId);
     332    if (InspectorDOMAgent* domAgent = inspectorDOMAgent())
     333        domAgent->removeNode(callId, nodeId);
     334}
     335
     336void InspectorBackend::changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded)
     337{
     338    if (InspectorDOMAgent* domAgent = inspectorDOMAgent())
     339        domAgent->changeTagName(callId, nodeId, tagName, expanded);
    357340}
    358341
  • trunk/WebCore/inspector/InspectorBackend.h

    r56107 r56683  
    77 *
    88 * 1.  Redistributions of source code must retain the above copyright
    9  *     notice, this list of conditions and the following disclaimer. 
     9 *     notice, this list of conditions and the following disclaimer.
    1010 * 2.  Redistributions in binary form must reproduce the above copyright
    1111 *     notice, this list of conditions and the following disclaimer in the
    12  *     documentation and/or other materials provided with the distribution. 
     12 *     documentation and/or other materials provided with the distribution.
    1313 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
    1414 *     its contributors may be used to endorse or promote products derived
    15  *     from this software without specific prior written permission. 
     15 *     from this software without specific prior written permission.
    1616 *
    1717 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     
    113113    void copyNode(long nodeId);
    114114    void removeNode(long callId, long nodeId);
     115    void changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded);
    115116
    116117    void getStyles(long callId, long nodeId, bool authOnly);
  • trunk/WebCore/inspector/InspectorBackend.idl

    r56107 r56683  
    8888        void copyNode(in long nodeId);
    8989        void removeNode(in long callId, in long nodeId);
     90        void changeTagName(in long callId, in long nodeId, in DOMString newTagName, in boolean expanded);
    9091        void highlightDOMNode(in long nodeId);
    9192        void hideDOMNodeHighlight();
  • trunk/WebCore/inspector/InspectorDOMAgent.cpp

    r56670 r56683  
    353353}
    354354
     355void InspectorDOMAgent::removeNode(long callId, long nodeId)
     356{
     357    Node* node = nodeForId(nodeId);
     358    if (!node) {
     359        // Use -1 to denote an error condition.
     360        m_frontend->didRemoveNode(callId, -1);
     361        return;
     362    }
     363
     364    Node* parentNode = node->parentNode();
     365    if (!parentNode) {
     366        m_frontend->didRemoveNode(callId, -1);
     367        return;
     368    }
     369
     370    ExceptionCode code;
     371    parentNode->removeChild(node, code);
     372    if (code) {
     373        m_frontend->didRemoveNode(callId, -1);
     374        return;
     375    }
     376
     377    m_frontend->didRemoveNode(callId, nodeId);
     378}
     379
     380void InspectorDOMAgent::changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded)
     381{
     382    Node* oldNode = nodeForId(nodeId);
     383    if (!oldNode || !oldNode->isElementNode()) {
     384        // Use -1 to denote an error condition.
     385        m_frontend->didChangeTagName(callId, -1);
     386        return;
     387    }
     388
     389    ExceptionCode code = 0;
     390    RefPtr<Element> newElem = oldNode->document()->createElement(tagName, code);
     391    if (code) {
     392        m_frontend->didChangeTagName(callId, -1);
     393        return;
     394    }
     395
     396    // Copy over the original node's attributes.
     397    Element* oldElem = static_cast<Element*>(oldNode);
     398    newElem->copyNonAttributeProperties(oldElem);
     399    if (oldElem->attributes())
     400        newElem->attributes()->setAttributes(*(oldElem->attributes(true)));
     401
     402    // Copy over the original node's children.
     403    Node* child;
     404    while ((child = oldNode->firstChild()))
     405        newElem->appendChild(child, code);
     406
     407    // Replace the old node with the new node
     408    Node* parent = oldNode->parentNode();
     409    parent->insertBefore(newElem, oldNode->nextSibling(), code);
     410    parent->removeChild(oldNode, code);
     411
     412    if (code) {
     413        m_frontend->didChangeTagName(callId, -1);
     414        return;
     415    }
     416
     417    long newId = pushNodePathToFrontend(newElem.get());
     418    if (expanded)
     419        pushChildNodesToFrontend(newId);
     420    m_frontend->didChangeTagName(callId, newId);
     421}
     422
    355423void InspectorDOMAgent::setTextNodeValue(long callId, long nodeId, const String& value)
    356424{
  • trunk/WebCore/inspector/InspectorDOMAgent.h

    r56608 r56683  
    9797        void setAttribute(long callId, long elementId, const String& name, const String& value);
    9898        void removeAttribute(long callId, long elementId, const String& name);
     99        void removeNode(long callId, long nodeId);
     100        void changeTagName(long callId, long nodeId, const AtomicString& tagName, bool expanded);
    99101        void setTextNodeValue(long callId, long nodeId, const String& value);
    100102        void getEventListenersForNode(long callId, long nodeId);
  • trunk/WebCore/inspector/InspectorFrontend.cpp

    r56670 r56683  
    471471}
    472472
     473void InspectorFrontend::didChangeTagName(long callId, long nodeId)
     474{
     475    ScriptFunctionCall function(m_webInspector, "dispatch");
     476    function.appendArgument("didChangeTagName");
     477    function.appendArgument(callId);
     478    function.appendArgument(nodeId);
     479    function.call();
     480}
     481
    473482void InspectorFrontend::didGetChildNodes(long callId)
    474483{
  • trunk/WebCore/inspector/InspectorFrontend.h

    r56670 r56683  
    133133        void didGetEventListenersForNode(long callId, long nodeId, const ScriptArray& listenersArray);
    134134        void didRemoveNode(long callId, long nodeId);
     135        void didChangeTagName(long callId, long nodeId);
    135136
    136137        void didGetStyles(long callId, const ScriptValue& styles);
  • trunk/WebCore/inspector/front-end/DOMAgent.js

    r56383 r56683  
    677677WebInspector.didSetTextNodeValue = WebInspector.Callback.processCallback;
    678678WebInspector.didRemoveNode = WebInspector.Callback.processCallback;
     679WebInspector.didChangeTagName = WebInspector.Callback.processCallback;
    679680WebInspector.didGetEventListenersForNode = WebInspector.Callback.processCallback;
    680681
  • trunk/WebCore/inspector/front-end/ElementsTreeOutline.js

    r56446 r56683  
    219219        return element;
    220220    },
    221    
     221
    222222    _keyDown: function(event)
    223223    {
     
    478478        this.updateChildren();
    479479    },
    480    
     480
    481481    updateChildren: function(fullRefresh)
    482482    {
     
    671671            return;
    672672
    673         if (this.treeOutline.showInElementsPanelEnabled) {   
     673        if (this.treeOutline.showInElementsPanelEnabled) {
    674674            WebInspector.showElementsPanel();
    675675            WebInspector.panels.elements.focusedDOMNode = this.representedObject;
     
    724724            return this._startEditingAttribute(attribute, event.target);
    725725
     726        if (this.treeOutline.isXMLMimeType || !WebInspector.ElementsTreeElement.EditTagBlacklist[this.representedObject.localName]) {
     727            var tagName = event.target.enclosingNodeOrSelfWithClass("webkit-html-tag-name");
     728            if (tagName)
     729                return this._startEditingTagName(tagName);
     730        }
     731
    726732        var newAttribute = event.target.enclosingNodeOrSelfWithClass("add-attribute");
    727733        if (newAttribute)
     
    742748        contextMenu.appendSeparator();
    743749
    744         // Add node-related actions.
     750        // Add free-form node-related actions.
    745751        contextMenu.appendItem(WebInspector.UIString("Edit as HTML"), this._editAsHTML.bind(this));
    746752        contextMenu.appendItem(WebInspector.UIString("Copy as HTML"), this._copyHTML.bind(this));
     
    857863        window.getSelection().setBaseAndExtent(textNode, 0, textNode, 1);
    858864
     865        return true;
     866    },
     867
     868    _startEditingTagName: function(tagNameElement)
     869    {
     870        if (WebInspector.isBeingEdited(tagNameElement))
     871            return true;
     872
     873        this._editing = true;
     874
     875        var closingTagElement = this._distinctClosingTagElement();
     876
     877        function keyupListener(event)
     878        {
     879            if (closingTagElement)
     880                closingTagElement.textContent = "</" + tagNameElement.textContent + ">";
     881        }
     882
     883        function editingComitted(element, newTagName)
     884        {
     885            tagNameElement.removeEventListener('keyup', keyupListener, false);
     886            this._tagNameEditingCommitted.apply(this, arguments);
     887        }
     888
     889        function editingCancelled()
     890        {
     891            tagNameElement.removeEventListener('keyup', keyupListener, false);
     892            this._editingCancelled.apply(this, arguments);
     893        }
     894
     895        tagNameElement.addEventListener('keyup', keyupListener, false);
     896
     897        WebInspector.startEditing(tagNameElement, editingComitted.bind(this), editingCancelled.bind(this), tagNameElement.textContent);
     898        window.getSelection().setBaseAndExtent(tagNameElement, 0, tagNameElement, 1);
    859899        return true;
    860900    },
     
    947987                this._triggerEditAttribute(moveToAttribute);
    948988            else if (newAttribute)
    949                 this._addNewAttribute(this.listItemElement);
     989                this._addNewAttribute();
    950990        }
    951991
     
    9831023    },
    9841024
     1025    _tagNameEditingCommitted: function(element, newText, oldText, tagName, moveDirection)
     1026    {
     1027        delete this._editing;
     1028        var self = this;
     1029
     1030        function cancel()
     1031        {
     1032            var closingTagElement = self._distinctClosingTagElement();
     1033            if (closingTagElement)
     1034                closingTagElement.textContent = "</" + tagName + ">";
     1035
     1036            self._editingCancelled(element, tagName);
     1037            moveToNextAttributeIfNeeded.call(self);
     1038        }
     1039
     1040        function moveToNextAttributeIfNeeded()
     1041        {
     1042            if (moveDirection !== "forward")
     1043                return;
     1044
     1045            var attributes = this.representedObject.attributes;
     1046            if (attributes.length > 0)
     1047                this._triggerEditAttribute(attributes[0].name);
     1048            else
     1049                this._addNewAttribute();
     1050        }
     1051
     1052        newText = newText.trim();
     1053        if (newText === oldText) {
     1054            cancel();
     1055            return;
     1056        }
     1057
     1058        var treeOutline = this.treeOutline;
     1059        var wasExpanded = this.expanded;
     1060
     1061        function changeTagNameCallback(nodeId)
     1062        {
     1063            if (nodeId === -1) {
     1064                cancel();
     1065                return;
     1066            }
     1067
     1068            // Select it and expand if necessary. We force tree update so that it processes dom events and is up to date.
     1069            WebInspector.panels.elements.updateModifiedNodes();
     1070
     1071            WebInspector.updateFocusedNode(nodeId);
     1072            var newTreeItem = treeOutline.findTreeElement(WebInspector.domAgent.nodeForId(nodeId));
     1073            if (wasExpanded)
     1074                newTreeItem.expand();
     1075
     1076            moveToNextAttributeIfNeeded.call(newTreeItem);
     1077        }
     1078
     1079        var callId = WebInspector.Callback.wrap(changeTagNameCallback);
     1080        InspectorBackend.changeTagName(callId, this.representedObject.id, newText, wasExpanded);
     1081    },
     1082
    9851083    _textNodeEditingCommitted: function(element, newText)
    9861084    {
     
    10091107    },
    10101108
     1109    _distinctClosingTagElement: function()
     1110    {
     1111        // FIXME: Improve the Tree Element / Outline Abstraction to prevent crawling the DOM
     1112
     1113        // For an expanded element, it will be the last element with class "close"
     1114        // in the child element list.
     1115        if (this.expanded) {
     1116            var closers = this._childrenListNode.querySelectorAll(".close");
     1117            return closers[closers.length-1];
     1118        }
     1119
     1120        // Remaining cases are single line non-expanded elements with a closing
     1121        // tag, or HTML elements without a closing tag (such as <br>). Return
     1122        // null in the case where there isn't a closing tag.
     1123        var tags = this.listItemElement.getElementsByClassName("webkit-html-tag");
     1124        return (tags.length === 1 ? null : tags[tags.length-1]);
     1125    },
     1126
    10111127    updateTitle: function()
    10121128    {
     
    10611177        var node = this.representedObject;
    10621178        var info = {title: "", hasChildren: this.hasChildren};
    1063        
     1179
    10641180        switch (node.nodeType) {
    10651181            case Node.DOCUMENT_NODE:
    10661182                info.title = "Document";
    10671183                break;
    1068                
     1184
    10691185            case Node.DOCUMENT_FRAGMENT_NODE:
    10701186                info.title = "Document Fragment";
     
    10731189            case Node.ELEMENT_NODE:
    10741190                var tagName = this.treeOutline.nodeNameToCorrectCase(node.nodeName).escapeHTML();
    1075                 info.title = "<span class=\"webkit-html-tag\">&lt;" + tagName;
    1076                
     1191                info.title = "<span class=\"webkit-html-tag\">&lt;";
     1192                info.title += "<span class=\"webkit-html-tag-name\">" + tagName + "</span>";
    10771193                if (node.hasAttributes()) {
    10781194                    for (var i = 0; i < node.attributes.length; ++i) {
    10791195                        var attr = node.attributes[i];
    10801196                        info.title += " <span class=\"webkit-html-attribute\"><span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=&#8203;\"";
    1081                        
     1197
    10821198                        var value = attr.value;
    10831199                        if (linkify && (attr.name === "src" || attr.name === "href")) {
     
    10931209                }
    10941210                info.title += "&gt;</span>&#8203;";
    1095                
     1211
    10961212                const closingTagHTML = "<span class=\"webkit-html-tag\">&lt;/" + tagName + "&gt;</span>&#8203;";
    10971213                var textChild = onlyTextChild.call(node);
     
    11121228                }
    11131229                break;
    1114                
     1230
    11151231            case Node.TEXT_NODE:
    11161232                if (isNodeWhitespace.call(node))
     
    11311247                        var cssSyntaxHighlighter = new WebInspector.DOMSyntaxHighlighter("text/css");
    11321248                        cssSyntaxHighlighter.syntaxHighlightNode(newNode);
    1133                        
     1249
    11341250                        info.title = "<span class=\"webkit-html-text-node webkit-html-css-node\">" + newNode.innerHTML.replace(/^[\n\r]*/, "").replace(/\s*$/, "") + "</span>";
    11351251                    } else {
    1136                         info.title = "\"<span class=\"webkit-html-text-node\">" + node.nodeValue.escapeHTML() + "</span>\""; 
     1252                        info.title = "\"<span class=\"webkit-html-text-node\">" + node.nodeValue.escapeHTML() + "</span>\"";
    11371253                    }
    1138                 } 
     1254                }
    11391255                break;
    11401256
     
    12481364
    12491365WebInspector.ElementsTreeElement.prototype.__proto__ = TreeElement.prototype;
     1366
     1367WebInspector.ElementsTreeElement.EditTagBlacklist = ["html", "head", "body"].keySet();
  • trunk/WebCore/inspector/front-end/treeoutline.js

    r53053 r56683  
    288288        return cachedElement;
    289289
    290     // The representedObject isn't know, so we start at the top of the tree and work down to find the first
     290    // The representedObject isn't known, so we start at the top of the tree and work down to find the first
    291291    // tree element that represents representedObject or one of its ancestors.
    292292    var item;
Note: See TracChangeset for help on using the changeset viewer.