| /* |
| * 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.<WebInspector.RemoteObjectProperty>)} callback |
| */ |
| getOwnProperties: function(callback) |
| { |
| this._getProperties(true, callback); |
| }, |
| |
| /** |
| * @param {function(Array.<WebInspector.RemoteObjectProperty>)} callback |
| */ |
| getAllProperties: function(callback) |
| { |
| this._getProperties(false, callback); |
| }, |
| |
| /** |
| * @param {boolean} ownProperties |
| * @param {function(Array.<RuntimeAgent.RemoteObject>)} callback |
| */ |
| _getProperties: function(ownProperties, callback) |
| { |
| if (!this._objectId) { |
| callback([]); |
| return; |
| } |
| |
| /** |
| * @param {?Protocol.Error} error |
| * @param {Array.<WebInspector.RemoteObjectProperty>} 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.<WebInspector.RemoteObjectProperty>)} callback |
| */ |
| getOwnProperties: function(callback) |
| { |
| callback(this._children()); |
| }, |
| |
| /** |
| * @param {function(Array.<WebInspector.RemoteObjectProperty>)} callback |
| */ |
| getAllProperties: function(callback) |
| { |
| callback(this._children()); |
| }, |
| |
| /** |
| * @return {Array.<WebInspector.RemoteObjectProperty>} |
| */ |
| _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; |
| } |
| } |