2010-12-01 Ilya Tikhonovsky <loislo@chromium.org>
[WebKit-https.git] / WebCore / inspector / front-end / DOMAgent.js
index 680ae5a..24e29ec 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Joseph Pecoraro
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
 WebInspector.DOMNode = function(doc, payload) {
     this.ownerDocument = doc;
 
-    this._id = payload.id;
+    this.id = payload.id;
     this.nodeType = payload.nodeType;
     this.nodeName = payload.nodeName;
+    this.localName = payload.localName;
     this._nodeValue = payload.nodeValue;
     this.textContent = this.nodeValue;
 
@@ -48,14 +50,38 @@ WebInspector.DOMNode = function(doc, payload) {
     this.nextSibling = null;
     this.prevSibling = null;
     this.firstChild = null;
+    this.lastChild = null;
     this.parentNode = null;
 
-    if (payload.childNodes)
-        this._setChildrenPayload(payload.childNodes);
+    if (payload.children)
+        this._setChildrenPayload(payload.children);
 
     this._computedStyle = null;
     this.style = null;
     this._matchedCSSRules = [];
+
+    this.breakpoints = {};
+
+    if (this.nodeType === Node.ELEMENT_NODE) {
+        // HTML and BODY from internal iframes should not overwrite top-level ones.
+        if (!this.ownerDocument.documentElement && this.nodeName === "HTML")
+            this.ownerDocument.documentElement = this;
+        if (!this.ownerDocument.body && this.nodeName === "BODY")
+            this.ownerDocument.body = this;
+        if (payload.documentURL)
+            this.documentURL = payload.documentURL;
+    } else if (this.nodeType === Node.DOCUMENT_TYPE_NODE) {
+        this.publicId = payload.publicId;
+        this.systemId = payload.systemId;
+        this.internalSubset = payload.internalSubset;
+    } else if (this.nodeType === Node.DOCUMENT_NODE) {
+        this.documentURL = payload.documentURL;
+    } else if (this.nodeType === Node.ATTRIBUTE_NODE) {
+        this.name = payload.name;
+        this.value = payload.value;
+    }
+
+    InspectorBackend.registerDomainDispatcher("DOM", this);
 }
 
 WebInspector.DOMNode.prototype = {
@@ -64,7 +90,8 @@ WebInspector.DOMNode.prototype = {
         return this.attributes.length > 0;
     },
 
-    hasChildNodes: function()  {
+    hasChildNodes: function()
+    {
         return this._childNodeCount > 0;
     },
 
@@ -75,13 +102,7 @@ WebInspector.DOMNode.prototype = {
     set nodeValue(value) {
         if (this.nodeType != Node.TEXT_NODE)
             return;
-        var self = this;
-        var callback = function()
-        {
-            self._nodeValue = value;
-            self.textContent = value;
-        };
-        this.ownerDocument._domAgent.setTextNodeValueAsync(this, value, callback);
+        this.ownerDocument._domAgent.setTextNodeValueAsync(this, value, function() {});
     },
 
     getAttribute: function(name)
@@ -120,8 +141,22 @@ WebInspector.DOMNode.prototype = {
         this.ownerDocument._domAgent.removeAttributeAsync(this, name, callback);
     },
 
+    path: function()
+    {
+        var path = [];
+        var node = this;
+        while (node && "index" in node && node.nodeName.length) {
+            path.push([node.index, node.nodeName]);
+            node = node.parentNode;
+        }
+        path.reverse();
+        return path.join(",");
+    },
+
     _setAttributesPayload: function(attrs)
     {
+        this.attributes = [];
+        this._attributesMap = {};
         for (var i = 0; i < attrs.length; i += 2)
             this._addAttribute(attrs[i], attrs[i + 1]);
     },
@@ -129,10 +164,13 @@ WebInspector.DOMNode.prototype = {
     _insertChild: function(prev, payload)
     {
         var node = new WebInspector.DOMNode(this.ownerDocument, payload);
-        if (!prev)
-            // First node
-            this.children = [ node ];
-        else
+        if (!prev) {
+            if (!this.children) {
+                // First node
+                this.children = [ node ];
+            } else
+                this.children.unshift(node);
+        } else
             this.children.splice(this.children.indexOf(prev) + 1, 0, node);
         this._renumber();
         return node;
@@ -161,11 +199,14 @@ WebInspector.DOMNode.prototype = {
         this._childNodeCount = this.children.length;
         if (this._childNodeCount == 0) {
             this.firstChild = null;
+            this.lastChild = null;
             return;
         }
         this.firstChild = this.children[0];
+        this.lastChild = this.children[this._childNodeCount - 1];
         for (var i = 0; i < this._childNodeCount; ++i) {
             var child = this.children[i];
+            child.index = i;
             child.nextSibling = i + 1 < this._childNodeCount ? this.children[i + 1] : null;
             child.prevSibling = i - 1 >= 0 ? this.children[i - 1] : null;
             child.parentNode = this;
@@ -181,69 +222,12 @@ WebInspector.DOMNode.prototype = {
         };
         this._attributesMap[name] = attr;
         this.attributes.push(attr);
-    },
-
-    _setStyles: function(computedStyle, inlineStyle, styleAttributes, matchedCSSRules)
-    {
-        this._computedStyle = this._makeStyle(computedStyle);
-        this.style = this._makeStyle(inlineStyle);
-
-        for (var name in styleAttributes) {
-            if (this._attributesMap[name])
-                this._attributesMap[name].style = this._makeStyle(styleAttributes[name]);
-        }
-
-        this._matchedCSSRules = [];
-        for (var i = 0; i < matchedCSSRules.length; i++) {
-            var description = matchedCSSRules[i];
-
-            var rule = {};
-            rule.selectorText = description.selectorText;
-            rule.style = this._makeStyle(description.style);
-
-            if (description.parentStyleSheet) {
-                var parentStyleMock = {};
-                parentStyleMock.href = description.parentStyleSheet.href;
-                var nodeName = description.parentStyleSheet.ownerNodeName;
-                if (nodeName) {
-                    parentStyleMock.ownerNode = {
-                        "nodeName": nodeName
-                    };
-                }
-                rule.parentStyleSheet = parentStyleMock;
-            }
-            this._matchedCSSRules.push(rule);
-        }
-    },
-
-    _makeStyle: function(payload)
-    {
-        var style = new WebInspector.CSSStyleDeclaration(payload);
-        style._nodeId = this._id;
-        return style;
-    },
-
-    _clearStyles: function()
-    {
-        this.computedStyle = null;
-        this.style = null;
-        for (var name in this._attributesMap)
-            this._attributesMap[name].style = null;
-        this._matchedCSSRules = null;
     }
 }
 
-WebInspector.DOMDocument = function(domAgent, defaultView)
+WebInspector.DOMDocument = function(domAgent, defaultView, payload)
 {
-    WebInspector.DOMNode.call(this, null,
-        {
-            id: 0,
-            nodeType: Node.DOCUMENT_NODE,
-            nodeName: "",
-            nodeValue: "",
-            attributes: [],
-            childNodeCount: 0
-        });
+    WebInspector.DOMNode.call(this, this, payload);
     this._listeners = {};
     this._domAgent = domAgent;
     this.defaultView = defaultView;
@@ -251,7 +235,7 @@ WebInspector.DOMDocument = function(domAgent, defaultView)
 
 WebInspector.DOMDocument.prototype = {
 
-    addEventListener: function(name, callback, useCapture)
+    addEventListener: function(name, callback)
     {
         var listeners = this._listeners[name];
         if (!listeners) {
@@ -261,7 +245,7 @@ WebInspector.DOMDocument.prototype = {
         listeners.push(callback);
     },
 
-    removeEventListener: function(name, callback, useCapture)
+    removeEventListener: function(name, callback)
     {
         var listeners = this._listeners[name];
         if (!listeners)
@@ -276,10 +260,12 @@ WebInspector.DOMDocument.prototype = {
     {
         var listeners = this._listeners[name];
         if (!listeners)
-          return;
+            return;
 
-        for (var i = 0; i < listeners.length; ++i)
-          listeners[i](event);
+        for (var i = 0; i < listeners.length; ++i) {
+            var listener = listeners[i];
+            listener.call(this, event);
+        }
     }
 }
 
@@ -296,6 +282,7 @@ WebInspector.DOMWindow.prototype = {
     {
         return this._domAgent.document;
     },
+
     get Node()
     {
         return WebInspector.DOMNode;
@@ -308,16 +295,6 @@ WebInspector.DOMWindow.prototype = {
 
     Object: function()
     {
-    },
-
-    getComputedStyle: function(node)
-    {
-        return node._computedStyle;
-    },
-
-    getMatchedCSSRules: function(node, pseudoElement, authorOnly)
-    {
-        return node._matchedCSSRules;
     }
 }
 
@@ -325,68 +302,43 @@ WebInspector.DOMAgent = function() {
     this._window = new WebInspector.DOMWindow(this);
     this._idToDOMNode = null;
     this.document = null;
-
-    // Install onpopulate handler. This is a temporary measure.
-    // TODO: add this code into the original updateChildren once domAgent
-    // becomes primary source of DOM information.
-    // TODO2: update ElementsPanel to not track embedded iframes - it is already being handled
-    // in the agent backend.
-    var domAgent = this;
-    var originalUpdateChildren = WebInspector.ElementsTreeElement.prototype.updateChildren;
-    WebInspector.ElementsTreeElement.prototype.updateChildren = function()
-    {
-        domAgent.getChildNodesAsync(this.representedObject, originalUpdateChildren.bind(this));
-    };
-
-    // Mute console handle to avoid crash on selection change.
-    // TODO: Re-implement inspectorConsoleAPI to work in a serialized way and remove this workaround.
-    WebInspector.Console.prototype.addInspectedNode = function()
-    {
-    };
-
-    // Whitespace is ignored in InspectorDOMAgent already -> no need to filter.
-    // TODO: Either remove all of its usages or push value into the agent backend.
-    Preferences.ignoreWhitespace = false;
 }
 
 WebInspector.DOMAgent.prototype = {
-    get inspectedWindow()
+    get domWindow()
     {
         return this._window;
     },
 
-    getChildNodesAsync: function(parent, opt_callback)
+    getChildNodesAsync: function(parent, callback)
     {
         var children = parent.children;
-        if (children && opt_callback) {
-          opt_callback(children);
-          return;
+        if (children) {
+            callback(children);
+            return;
         }
-        var mycallback = function() {
-            if (opt_callback) {
-                opt_callback(parent.children);
-            }
-        };
-        var callId = WebInspector.Callback.wrap(mycallback);
-        InspectorController.getChildNodes(callId, parent._id);
+        function mycallback() {
+            callback(parent.children);
+        }
+        InspectorBackend.getChildNodes(parent.id, mycallback);
     },
 
     setAttributeAsync: function(node, name, value, callback)
     {
         var mycallback = this._didApplyDomChange.bind(this, node, callback);
-        InspectorController.setAttribute(WebInspector.Callback.wrap(mycallback), node._id, name, value);
+        InspectorBackend.setAttribute(node.id, name, value, mycallback);
     },
 
     removeAttributeAsync: function(node, name, callback)
     {
         var mycallback = this._didApplyDomChange.bind(this, node, callback);
-        InspectorController.removeAttribute(WebInspector.Callback.wrap(mycallback), node._id, name);
+        InspectorBackend.removeAttribute(node.id, name, mycallback);
     },
 
     setTextNodeValueAsync: function(node, text, callback)
     {
         var mycallback = this._didApplyDomChange.bind(this, node, callback);
-        InspectorController.setTextNodeValue(WebInspector.Callback.wrap(mycallback), node._id, text);
+        InspectorBackend.setTextNodeValue(node.id, text, mycallback);
     },
 
     _didApplyDomChange: function(node, callback, success)
@@ -396,37 +348,54 @@ WebInspector.DOMAgent.prototype = {
         callback();
         // TODO(pfeldman): Fix this hack.
         var elem = WebInspector.panels.elements.treeOutline.findTreeElement(node);
-        if (elem) {
-            elem._updateTitle();
-        }
+        if (elem)
+            elem.updateTitle();
     },
 
     _attributesUpdated: function(nodeId, attrsArray)
     {
         var node = this._idToDOMNode[nodeId];
         node._setAttributesPayload(attrsArray);
+        var event = {target: node};
+        this.document._fireDomEvent("DOMAttrModified", event);
     },
 
-    getNodeForId: function(nodeId) {
+    _characterDataModified: function(nodeId, newValue)
+    {
+        var node = this._idToDOMNode[nodeId];
+        node._nodeValue = newValue;
+        node.textContent = newValue;
+        var event = { target : node };
+        this.document._fireDomEvent("DOMCharacterDataModified", event);
+    },
+
+    nodeForId: function(nodeId)
+    {
         return this._idToDOMNode[nodeId];
     },
 
-    _setDocumentElement: function(payload)
+    _setDocument: function(payload)
+    {
+        this._idToDOMNode = {};
+        if (payload && "id" in payload) {
+            this.document = new WebInspector.DOMDocument(this, this._window, payload);
+            this._idToDOMNode[payload.id] = this.document;
+            this._bindNodes(this.document.children);
+            WebInspector.breakpointManager.restoreDOMBreakpoints();
+        } else
+            this.document = null;
+        WebInspector.panels.elements.setDocument(this.document);
+    },
+
+    _setDetachedRoot: function(payload)
     {
-        this.document = new WebInspector.DOMDocument(this, this._window);
-        this._idToDOMNode = { 0 : this.document };
-        this._setChildNodes(0, [payload]);
-        this.document.documentElement = this.document.firstChild;
-        this.document.documentElement.ownerDocument = this.document;
-        WebInspector.panels.elements.reset();
+        var root = new WebInspector.DOMNode(this.document, payload);
+        this._idToDOMNode[payload.id] = root;
     },
 
     _setChildNodes: function(parentId, payloads)
     {
         var parent = this._idToDOMNode[parentId];
-        if (parent.children) {
-          return;
-        }
         parent._setChildrenPayload(payloads);
         this._bindNodes(parent.children);
     },
@@ -435,21 +404,20 @@ WebInspector.DOMAgent.prototype = {
     {
         for (var i = 0; i < children.length; ++i) {
             var child = children[i];
-            this._idToDOMNode[child._id] = child;
+            this._idToDOMNode[child.id] = child;
             if (child.children)
                 this._bindNodes(child.children);
         }
     },
 
-    _hasChildrenUpdated: function(nodeId, newValue)
+    _childNodeCountUpdated: function(nodeId, newValue)
     {
         var node = this._idToDOMNode[nodeId];
+        node._childNodeCount = newValue;
         var outline = WebInspector.panels.elements.treeOutline;
         var treeElement = outline.findTreeElement(node);
-        if (treeElement) {
+        if (treeElement)
             treeElement.hasChildren = newValue;
-            treeElement.whitespaceIgnored = Preferences.ignoreWhitespace;
-        }
     },
 
     _childNodeInserted: function(parentId, prevId, payload)
@@ -457,7 +425,7 @@ WebInspector.DOMAgent.prototype = {
         var parent = this._idToDOMNode[parentId];
         var prev = this._idToDOMNode[prevId];
         var node = parent._insertChild(prev, payload);
-        this._idToDOMNode[node._id] = node;
+        this._idToDOMNode[node.id] = node;
         var event = { target : node, relatedNode : parent };
         this.document._fireDomEvent("DOMNodeInserted", event);
     },
@@ -470,52 +438,92 @@ WebInspector.DOMAgent.prototype = {
         var event = { target : node, relatedNode : parent };
         this.document._fireDomEvent("DOMNodeRemoved", event);
         delete this._idToDOMNode[nodeId];
+        this._removeBreakpoints(node);
+    },
+
+    _removeBreakpoints: function(node)
+    {
+        for (var type in node.breakpoints)
+            node.breakpoints[type].remove();
+        if (!node.children)
+            return;
+        for (var i = 0; i < node.children.length; ++i)
+            this._removeBreakpoints(node.children[i]);
     }
 }
 
-WebInspector.CSSStyleDeclaration = function(payload) {
-    this._id = payload.id;
-    this.width = payload.width;
-    this.height = payload.height;
-    this.__disabledProperties = payload.disabledProperties;
-    this.__disabledPropertyValues = payload.disabledPropertyValues;
-    this.__disabledPropertyPriorities = payload.disabledPropertyPriorities;
-
-    this._propertyMap = {};
-    this.length = this._properties.length;
-
-    for (var i = 0; i < this.length; ++i) {
-        var property = this._properties[i];
-        var name = property.name;
-        this[i] = name;
-        this._propertyMap[name] = property;
+WebInspector.ApplicationCache = {}
+
+WebInspector.ApplicationCache.getApplicationCachesAsync = function(callback)
+{
+    function mycallback(applicationCaches)
+    {
+        // FIXME: Currently, this list only returns a single application cache.
+        if (applicationCaches)
+            callback(applicationCaches);
     }
+
+    InspectorBackend.getApplicationCaches(mycallback);
 }
 
-WebInspector.CSSStyleDeclaration.prototype = {
-    getPropertyValue: function(name)
-    {
-        var property = this._propertyMap[name];
-        return property ? property.value : "";
-    },
+WebInspector.Cookies = {}
 
-    getPropertyPriority: function(name)
+WebInspector.Cookies.getCookiesAsync = function(callback)
+{
+    function mycallback(cookies, cookiesString)
     {
-        var property = this._propertyMap[name];
-        return property ? property.priority : "";
-    },
+        if (cookiesString)
+            callback(WebInspector.Cookies.buildCookiesFromString(cookiesString), false);
+        else
+            callback(cookies, true);
+    }
 
-    getPropertyShorthand: function(name)
-    {
-        var property = this._propertyMap[name];
-        return property ? property.shorthand : "";
-    },
+    InspectorBackend.getCookies(mycallback);
+}
 
-    isPropertyImplicit: function(name)
-    {
-        var property = this._propertyMap[name];
-        return property ? property.implicit : "";
+WebInspector.Cookies.buildCookiesFromString = function(rawCookieString)
+{
+    var rawCookies = rawCookieString.split(/;\s*/);
+    var cookies = [];
+
+    if (!(/^\s*$/.test(rawCookieString))) {
+        for (var i = 0; i < rawCookies.length; ++i) {
+            var cookie = rawCookies[i];
+            var delimIndex = cookie.indexOf("=");
+            var name = cookie.substring(0, delimIndex);
+            var value = cookie.substring(delimIndex + 1);
+            var size = name.length + value.length;
+            cookies.push({ name: name, value: value, size: size });
+        }
     }
+
+    return cookies;
+}
+
+WebInspector.Cookies.cookieMatchesResourceURL = function(cookie, resourceURL)
+{
+    var url = resourceURL.asParsedURL();
+    if (!url || !this.cookieDomainMatchesResourceDomain(cookie.domain, url.host))
+        return false;
+    return (url.path.indexOf(cookie.path) === 0
+        && (!cookie.port || url.port == cookie.port)
+        && (!cookie.secure || url.scheme === "https"));
+}
+
+WebInspector.Cookies.cookieDomainMatchesResourceDomain = function(cookieDomain, resourceDomain)
+{
+    if (cookieDomain.charAt(0) !== '.')
+        return resourceDomain === cookieDomain;
+    return !!resourceDomain.match(new RegExp("^([^\\.]+\\.)?" + cookieDomain.substring(1).escapeForRegExp() + "$"), "i");
+}
+
+WebInspector.EventListeners = {}
+
+WebInspector.EventListeners.getEventListenersForNodeAsync = function(node, callback)
+{
+    if (!node)
+        return;
+    InspectorBackend.getEventListenersForNode(node.id, callback);
 }
 
 WebInspector.attributesUpdated = function()
@@ -523,9 +531,19 @@ WebInspector.attributesUpdated = function()
     this.domAgent._attributesUpdated.apply(this.domAgent, arguments);
 }
 
-WebInspector.setDocumentElement = function()
+WebInspector.characterDataModified = function()
+{
+    this.domAgent._characterDataModified.apply(this.domAgent, arguments);
+}
+
+WebInspector.setDocument = function()
+{
+    this.domAgent._setDocument.apply(this.domAgent, arguments);
+}
+
+WebInspector.setDetachedRoot = function()
 {
-    this.domAgent._setDocumentElement.apply(this.domAgent, arguments);
+    this.domAgent._setDetachedRoot.apply(this.domAgent, arguments);
 }
 
 WebInspector.setChildNodes = function()
@@ -533,25 +551,17 @@ WebInspector.setChildNodes = function()
     this.domAgent._setChildNodes.apply(this.domAgent, arguments);
 }
 
-WebInspector.hasChildrenUpdated = function()
+WebInspector.childNodeCountUpdated = function()
 {
-    this.domAgent._hasChildrenUpdated.apply(this.domAgent, arguments);
+    this.domAgent._childNodeCountUpdated.apply(this.domAgent, arguments);
 }
 
 WebInspector.childNodeInserted = function()
 {
     this.domAgent._childNodeInserted.apply(this.domAgent, arguments);
-    this._childNodeInserted.bind(this);
 }
 
 WebInspector.childNodeRemoved = function()
 {
     this.domAgent._childNodeRemoved.apply(this.domAgent, arguments);
-    this._childNodeRemoved.bind(this);
 }
-
-WebInspector.didGetChildNodes = WebInspector.Callback.processCallback;
-WebInspector.didPerformSearch = WebInspector.Callback.processCallback;
-WebInspector.didApplyDomChange = WebInspector.Callback.processCallback;
-WebInspector.didRemoveAttribute = WebInspector.Callback.processCallback;
-WebInspector.didSetTextNodeValue = WebInspector.Callback.processCallback;