/* * Copyright (C) 2009 Google Inc. All rights reserved. * * 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. */ /** * @constructor * @param {string|undefined} objectId * @param {string} type * @param {string|undefined} subtype * @param {*} value * @param {string=} description */ WebInspector.RemoteObject = function(objectId, type, subtype, value, description) { this._type = type; this._subtype = subtype; if (objectId) { // handle this._objectId = objectId; this._description = description; this._hasChildren = true; } else { // Primitive or null object. console.assert(type !== "object" || value === null); this._description = description || (value + ""); this._hasChildren = false; this.value = value; } } /** * @param {number|string|boolean} value * @return {WebInspector.RemoteObject} */ WebInspector.RemoteObject.fromPrimitiveValue = function(value) { return new WebInspector.RemoteObject(undefined, typeof value, undefined, value); } /** * @param {Object} value * @return {WebInspector.RemoteObject} */ WebInspector.RemoteObject.fromLocalObject = function(value) { return new WebInspector.LocalJSONObject(value); } /** * @param {WebInspector.DOMNode} node * @param {string} objectGroup * @param {function(?WebInspector.RemoteObject)} callback */ WebInspector.RemoteObject.resolveNode = function(node, objectGroup, callback) { /** * @param {?Protocol.Error} error * @param {RuntimeAgent.RemoteObject} object */ function mycallback(error, object) { if (!callback) return; if (error || !object) callback(null); else callback(WebInspector.RemoteObject.fromPayload(object)); } DOMAgent.resolveNode(node.id, objectGroup, mycallback); } /** * @param {RuntimeAgent.RemoteObject} payload * @return {WebInspector.RemoteObject} */ WebInspector.RemoteObject.fromPayload = function(payload) { console.assert(typeof payload === "object", "Remote object payload should only be an object"); return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description); } /** * @param {WebInspector.RemoteObject} remoteObject * @return {string} */ WebInspector.RemoteObject.type = function(remoteObject) { if (remoteObject === null) return "null"; var type = typeof remoteObject; if (type !== "object" && type !== "function") return type; return remoteObject.type; } WebInspector.RemoteObject.prototype = { /** @return {RuntimeAgent.RemoteObjectId} */ get objectId() { return this._objectId; }, /** @return {string} */ get type() { return this._type; }, /** @return {string|undefined} */ get subtype() { return this._subtype; }, /** @return {string|undefined} */ get description() { return this._description; }, /** @return {boolean} */ get hasChildren() { return this._hasChildren; }, /** * @param {function(Array.)} callback */ getOwnProperties: function(callback) { this._getProperties(true, callback); }, /** * @param {function(Array.)} callback */ getAllProperties: function(callback) { this._getProperties(false, callback); }, /** * @param {boolean} ownProperties * @param {function(Array.)} callback */ _getProperties: function(ownProperties, callback) { if (!this._objectId) { callback([]); return; } /** * @param {?Protocol.Error} error * @param {Array.} properties */ function remoteObjectBinder(error, properties) { if (error) { callback(null); return; } var result = []; for (var i = 0; properties && i < properties.length; ++i) { var property = properties[i]; if (property.get || property.set) { if (property.get) result.push(new WebInspector.RemoteObjectProperty("get " + property.name, WebInspector.RemoteObject.fromPayload(property.get), property)); if (property.set) result.push(new WebInspector.RemoteObjectProperty("set " + property.name, WebInspector.RemoteObject.fromPayload(property.set), property)); } else result.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value), property)); } callback(result); } RuntimeAgent.getProperties(this._objectId, ownProperties, remoteObjectBinder); }, /** * @param {string} name * @param {string} value * @param {function(string=)} callback */ setPropertyValue: function(name, value, callback) { if (!this._objectId) { callback("Can't set a property of non-object."); return; } RuntimeAgent.evaluate.invoke({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this)); /** * @param {?Protocol.Error} error * @param {RuntimeAgent.RemoteObject} result * @param {boolean=} wasThrown */ function evaluatedCallback(error, result, wasThrown) { if (error || wasThrown) { callback(error || result.description); return; } function setPropertyValue(propertyName, propertyValue) { this[propertyName] = propertyValue; } delete result.description; // Optimize on traffic. RuntimeAgent.callFunctionOn(this._objectId, setPropertyValue.toString(), [{ value:name }, result], true, undefined, propertySetCallback.bind(this)); if (result._objectId) RuntimeAgent.releaseObject(result._objectId); } /** * @param {?Protocol.Error} error * @param {RuntimeAgent.RemoteObject} result * @param {boolean=} wasThrown */ function propertySetCallback(error, result, wasThrown) { if (error || wasThrown) { callback(error || result.description); return; } callback(); } }, /** * @param {function(DOMAgent.NodeId)} callback */ pushNodeToFrontend: function(callback) { if (this._objectId) WebInspector.domTreeManager.pushNodeToFrontend(this._objectId, callback); else callback(0); }, /** * @param {string} functionDeclaration * @param {function(?WebInspector.RemoteObject)} callback */ callFunction: function(functionDeclaration, args, callback) { function mycallback(error, result, wasThrown) { callback((error || wasThrown) ? null : WebInspector.RemoteObject.fromPayload(result)); } RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, mycallback); }, /** * @param {string} functionDeclaration * @param {function(*)} callback */ callFunctionJSON: function(functionDeclaration, args, callback) { function mycallback(error, result, wasThrown) { callback((error || wasThrown) ? null : result.value); } RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, mycallback); }, release: function() { RuntimeAgent.releaseObject(this._objectId); }, /** * @return {number} */ arrayLength: function() { if (this.subtype !== "array") return 0; var matches = this._description.match(/\[([0-9]+)\]/); if (!matches) return 0; return parseInt(matches[1], 10); } } /** * @constructor * @param {string} name * @param {WebInspector.RemoteObject} value * @param {Object=} descriptor */ WebInspector.RemoteObjectProperty = function(name, value, descriptor) { this.name = name; this.value = value; this.enumerable = descriptor ? !!descriptor.enumerable : true; this.writable = descriptor ? !!descriptor.writable : true; if (descriptor && descriptor.wasThrown) this.wasThrown = true; } /** * @param {string} name * @param {string} value * @return {WebInspector.RemoteObjectProperty} */ WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value) { return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value)); } // The below is a wrapper around a local object that provides an interface comaptible // with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection). // Note that only JSON-compliant objects are currently supported, as there's no provision // for traversing prototypes, extracting class names via constuctor, handling properties // or functions. /** * @constructor * @extends {WebInspector.RemoteObject} * @param {Object} value */ WebInspector.LocalJSONObject = function(value) { this._value = value; } WebInspector.LocalJSONObject.prototype = { /** * @return {string} */ get description() { if (this._cachedDescription) return this._cachedDescription; if (this.type === "object") { switch (this.subtype) { case "array": function formatArrayItem(property) { return property.value.description; } this._cachedDescription = this._concatenate("[", "]", formatArrayItem); break; case "null": this._cachedDescription = "null"; break; default: function formatObjectItem(property) { return property.name + ":" + property.value.description; } this._cachedDescription = this._concatenate("{", "}", formatObjectItem); } } else this._cachedDescription = String(this._value); return this._cachedDescription; }, /** * @param {string} prefix * @param {string} suffix * @return {string} */ _concatenate: function(prefix, suffix, formatProperty) { const previewChars = 100; var buffer = prefix; var children = this._children(); for (var i = 0; i < children.length; ++i) { var itemDescription = formatProperty(children[i]); if (buffer.length + itemDescription.length > previewChars) { buffer += ",\u2026"; break; } if (i) buffer += ", "; buffer += itemDescription; } buffer += suffix; return buffer; }, /** * @return {string} */ get type() { return typeof this._value; }, /** * @return {string|undefined} */ get subtype() { if (this._value === null) return "null"; if (this._value instanceof Array) return "array"; return undefined; }, /** * @return {boolean} */ get hasChildren() { return typeof this._value === "object" && this._value !== null && !isEmptyObject(this._value); }, /** * @param {function(Array.)} callback */ getOwnProperties: function(callback) { callback(this._children()); }, /** * @param {function(Array.)} callback */ getAllProperties: function(callback) { callback(this._children()); }, /** * @return {Array.} */ _children: function() { if (!this.hasChildren) return []; function buildProperty(propName) { return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName])); } if (!this._cachedChildren) this._cachedChildren = Object.keys(this._value || {}).map(buildProperty.bind(this)); return this._cachedChildren; }, /** * @return {boolean} */ isError: function() { return false; } }