590 lines
17 KiB
JavaScript
590 lines
17 KiB
JavaScript
/*
|
|
* 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
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Google Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
WebInspector.DOMNode = function(doc, payload) {
|
|
this.ownerDocument = doc;
|
|
|
|
this.id = payload.id;
|
|
this.nodeType = payload.nodeType;
|
|
this.nodeName = payload.nodeName;
|
|
this.localName = payload.localName;
|
|
this._nodeValue = payload.nodeValue;
|
|
this.textContent = this.nodeValue;
|
|
|
|
this.attributes = [];
|
|
this._attributesMap = {};
|
|
if (payload.attributes)
|
|
this._setAttributesPayload(payload.attributes);
|
|
|
|
this._childNodeCount = payload.childNodeCount;
|
|
this.children = null;
|
|
|
|
this.nextSibling = null;
|
|
this.prevSibling = null;
|
|
this.firstChild = null;
|
|
this.lastChild = null;
|
|
this.parentNode = null;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
WebInspector.DOMNode.prototype = {
|
|
hasAttributes: function()
|
|
{
|
|
return this.attributes.length > 0;
|
|
},
|
|
|
|
hasChildNodes: function()
|
|
{
|
|
return this._childNodeCount > 0;
|
|
},
|
|
|
|
get nodeValue() {
|
|
return this._nodeValue;
|
|
},
|
|
|
|
set nodeValue(value) {
|
|
if (this.nodeType != Node.TEXT_NODE)
|
|
return;
|
|
this.ownerDocument._domAgent.setTextNodeValueAsync(this, value, function() {});
|
|
},
|
|
|
|
getAttribute: function(name)
|
|
{
|
|
var attr = this._attributesMap[name];
|
|
return attr ? attr.value : undefined;
|
|
},
|
|
|
|
setAttribute: function(name, value)
|
|
{
|
|
var self = this;
|
|
var callback = function()
|
|
{
|
|
var attr = self._attributesMap[name];
|
|
if (attr)
|
|
attr.value = value;
|
|
else
|
|
attr = self._addAttribute(name, value);
|
|
};
|
|
this.ownerDocument._domAgent.setAttributeAsync(this, name, value, callback);
|
|
},
|
|
|
|
removeAttribute: function(name)
|
|
{
|
|
var self = this;
|
|
var callback = function()
|
|
{
|
|
delete self._attributesMap[name];
|
|
for (var i = 0; i < self.attributes.length; ++i) {
|
|
if (self.attributes[i].name == name) {
|
|
self.attributes.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
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]);
|
|
},
|
|
|
|
_insertChild: function(prev, payload)
|
|
{
|
|
var node = new WebInspector.DOMNode(this.ownerDocument, payload);
|
|
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;
|
|
},
|
|
|
|
removeChild_: function(node)
|
|
{
|
|
this.children.splice(this.children.indexOf(node), 1);
|
|
node.parentNode = null;
|
|
this._renumber();
|
|
},
|
|
|
|
_setChildrenPayload: function(payloads)
|
|
{
|
|
this.children = [];
|
|
for (var i = 0; i < payloads.length; ++i) {
|
|
var payload = payloads[i];
|
|
var node = new WebInspector.DOMNode(this.ownerDocument, payload);
|
|
this.children.push(node);
|
|
}
|
|
this._renumber();
|
|
},
|
|
|
|
_renumber: function()
|
|
{
|
|
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;
|
|
}
|
|
},
|
|
|
|
_addAttribute: function(name, value)
|
|
{
|
|
var attr = {
|
|
"name": name,
|
|
"value": value,
|
|
"_node": this
|
|
};
|
|
this._attributesMap[name] = attr;
|
|
this.attributes.push(attr);
|
|
}
|
|
}
|
|
|
|
WebInspector.DOMDocument = function(domAgent, defaultView, payload)
|
|
{
|
|
WebInspector.DOMNode.call(this, this, payload);
|
|
this._listeners = {};
|
|
this._domAgent = domAgent;
|
|
this.defaultView = defaultView;
|
|
}
|
|
|
|
WebInspector.DOMDocument.prototype = {
|
|
|
|
addEventListener: function(name, callback)
|
|
{
|
|
var listeners = this._listeners[name];
|
|
if (!listeners) {
|
|
listeners = [];
|
|
this._listeners[name] = listeners;
|
|
}
|
|
listeners.push(callback);
|
|
},
|
|
|
|
removeEventListener: function(name, callback)
|
|
{
|
|
var listeners = this._listeners[name];
|
|
if (!listeners)
|
|
return;
|
|
|
|
var index = listeners.indexOf(callback);
|
|
if (index != -1)
|
|
listeners.splice(index, 1);
|
|
},
|
|
|
|
_fireDomEvent: function(name, event)
|
|
{
|
|
var listeners = this._listeners[name];
|
|
if (!listeners)
|
|
return;
|
|
|
|
for (var i = 0; i < listeners.length; ++i) {
|
|
var listener = listeners[i];
|
|
listener.call(this, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
WebInspector.DOMDocument.prototype.__proto__ = WebInspector.DOMNode.prototype;
|
|
|
|
|
|
WebInspector.DOMWindow = function(domAgent)
|
|
{
|
|
this._domAgent = domAgent;
|
|
}
|
|
|
|
WebInspector.DOMWindow.prototype = {
|
|
get document()
|
|
{
|
|
return this._domAgent.document;
|
|
},
|
|
|
|
get Node()
|
|
{
|
|
return WebInspector.DOMNode;
|
|
},
|
|
|
|
get Element()
|
|
{
|
|
return WebInspector.DOMNode;
|
|
},
|
|
|
|
Object: function()
|
|
{
|
|
}
|
|
}
|
|
|
|
WebInspector.DOMAgent = function() {
|
|
this._window = new WebInspector.DOMWindow(this);
|
|
this._idToDOMNode = null;
|
|
this.document = null;
|
|
InspectorBackend.registerDomainDispatcher("DOM", new WebInspector.DOMDispatcher(this));
|
|
}
|
|
|
|
WebInspector.DOMAgent.prototype = {
|
|
get domWindow()
|
|
{
|
|
return this._window;
|
|
},
|
|
|
|
getChildNodesAsync: function(parent, callback)
|
|
{
|
|
var children = parent.children;
|
|
if (children) {
|
|
callback(children);
|
|
return;
|
|
}
|
|
function mycallback() {
|
|
callback(parent.children);
|
|
}
|
|
InspectorBackend.getChildNodes(parent.id, mycallback);
|
|
},
|
|
|
|
setAttributeAsync: function(node, name, value, callback)
|
|
{
|
|
var mycallback = this._didApplyDomChange.bind(this, node, callback);
|
|
InspectorBackend.setAttribute(node.id, name, value, mycallback);
|
|
},
|
|
|
|
removeAttributeAsync: function(node, name, callback)
|
|
{
|
|
var mycallback = this._didApplyDomChange.bind(this, node, callback);
|
|
InspectorBackend.removeAttribute(node.id, name, mycallback);
|
|
},
|
|
|
|
setTextNodeValueAsync: function(node, text, callback)
|
|
{
|
|
var mycallback = this._didApplyDomChange.bind(this, node, callback);
|
|
InspectorBackend.setTextNodeValue(node.id, text, mycallback);
|
|
},
|
|
|
|
_didApplyDomChange: function(node, callback, success)
|
|
{
|
|
if (!success)
|
|
return;
|
|
callback();
|
|
// TODO(pfeldman): Fix this hack.
|
|
var elem = WebInspector.panels.elements.treeOutline.findTreeElement(node);
|
|
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);
|
|
},
|
|
|
|
_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];
|
|
},
|
|
|
|
_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)
|
|
{
|
|
var root = new WebInspector.DOMNode(this.document, payload);
|
|
this._idToDOMNode[payload.id] = root;
|
|
},
|
|
|
|
_setChildNodes: function(parentId, payloads)
|
|
{
|
|
var parent = this._idToDOMNode[parentId];
|
|
parent._setChildrenPayload(payloads);
|
|
this._bindNodes(parent.children);
|
|
},
|
|
|
|
_bindNodes: function(children)
|
|
{
|
|
for (var i = 0; i < children.length; ++i) {
|
|
var child = children[i];
|
|
this._idToDOMNode[child.id] = child;
|
|
if (child.children)
|
|
this._bindNodes(child.children);
|
|
}
|
|
},
|
|
|
|
_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)
|
|
treeElement.hasChildren = newValue;
|
|
},
|
|
|
|
_childNodeInserted: function(parentId, prevId, payload)
|
|
{
|
|
var parent = this._idToDOMNode[parentId];
|
|
var prev = this._idToDOMNode[prevId];
|
|
var node = parent._insertChild(prev, payload);
|
|
this._idToDOMNode[node.id] = node;
|
|
var event = { target : node, relatedNode : parent };
|
|
this.document._fireDomEvent("DOMNodeInserted", event);
|
|
},
|
|
|
|
_childNodeRemoved: function(parentId, nodeId)
|
|
{
|
|
var parent = this._idToDOMNode[parentId];
|
|
var node = this._idToDOMNode[nodeId];
|
|
parent.removeChild_(node);
|
|
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.DOMDispatcher = function(domAgent)
|
|
{
|
|
this._domAgent = domAgent;
|
|
}
|
|
|
|
WebInspector.DOMDispatcher.prototype = {
|
|
setDocument: function(payload)
|
|
{
|
|
this._domAgent._setDocument(payload);
|
|
},
|
|
|
|
attributesUpdated: function(nodeId, attrsArray)
|
|
{
|
|
this._domAgent._attributesUpdated(nodeId, attrsArray);
|
|
},
|
|
|
|
characterDataModified: function(nodeId, newValue)
|
|
{
|
|
this._domAgent._characterDataModified(nodeId, newValue);
|
|
},
|
|
|
|
setChildNodes: function(parentId, payloads)
|
|
{
|
|
this._domAgent._setChildNodes(parentId, payloads);
|
|
},
|
|
|
|
setDetachedRoot: function(payload)
|
|
{
|
|
this._domAgent._setDetachedRoot(payload);
|
|
},
|
|
|
|
childNodeCountUpdated: function(nodeId, newValue)
|
|
{
|
|
this._domAgent._childNodeCountUpdated(nodeId, newValue);
|
|
},
|
|
|
|
childNodeInserted: function(parentId, prevId, payload)
|
|
{
|
|
this._domAgent._childNodeInserted(parentId, prevId, payload);
|
|
},
|
|
|
|
childNodeRemoved: function(parentId, nodeId)
|
|
{
|
|
this._domAgent._childNodeRemoved(parentId, nodeId);
|
|
}
|
|
}
|
|
|
|
WebInspector.ApplicationCacheDispatcher = function()
|
|
{
|
|
}
|
|
|
|
WebInspector.ApplicationCacheDispatcher.getApplicationCachesAsync = function(callback)
|
|
{
|
|
function mycallback(applicationCaches)
|
|
{
|
|
// FIXME: Currently, this list only returns a single application cache.
|
|
if (applicationCaches)
|
|
callback(applicationCaches);
|
|
}
|
|
|
|
InspectorBackend.getApplicationCaches(mycallback);
|
|
}
|
|
|
|
WebInspector.ApplicationCacheDispatcher.prototype = {
|
|
updateApplicationCacheStatus: function(status)
|
|
{
|
|
WebInspector.panels.resources.updateApplicationCacheStatus(status);
|
|
},
|
|
|
|
updateNetworkState: function(isNowOnline)
|
|
{
|
|
WebInspector.panels.resources.updateNetworkState(isNowOnline);
|
|
}
|
|
}
|
|
|
|
InspectorBackend.registerDomainDispatcher("ApplicationCache", new WebInspector.ApplicationCacheDispatcher());
|
|
|
|
WebInspector.Cookies = {}
|
|
|
|
WebInspector.Cookies.getCookiesAsync = function(callback)
|
|
{
|
|
function mycallback(cookies, cookiesString)
|
|
{
|
|
if (cookiesString)
|
|
callback(WebInspector.Cookies.buildCookiesFromString(cookiesString), false);
|
|
else
|
|
callback(cookies, true);
|
|
}
|
|
|
|
InspectorBackend.getCookies(mycallback);
|
|
}
|
|
|
|
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);
|
|
}
|