RemoteObject.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. /*
  2. * Copyright (C) 2009 Google Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are
  6. * met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above
  11. * copyright notice, this list of conditions and the following disclaimer
  12. * in the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Google Inc. nor the names of its
  15. * contributors may be used to endorse or promote products derived from
  16. * this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /**
  31. * @constructor
  32. * @param {string|undefined} objectId
  33. * @param {string} type
  34. * @param {string|undefined} subtype
  35. * @param {*} value
  36. * @param {string=} description
  37. * @param {RuntimeAgent.ObjectPreview=} preview
  38. * @param {WebInspector.ScopeRef=} scopeRef
  39. */
  40. WebInspector.RemoteObject = function(objectId, type, subtype, value, description, preview, scopeRef)
  41. {
  42. this._type = type;
  43. this._subtype = subtype;
  44. if (objectId) {
  45. // handle
  46. this._objectId = objectId;
  47. this._description = description;
  48. this._hasChildren = true;
  49. this._preview = preview;
  50. } else {
  51. // Primitive or null object.
  52. console.assert(type !== "object" || value === null);
  53. this._description = description || (value + "");
  54. this._hasChildren = false;
  55. this.value = value;
  56. }
  57. this._scopeRef = scopeRef;
  58. }
  59. /**
  60. * @param {number|string|boolean} value
  61. * @return {WebInspector.RemoteObject}
  62. */
  63. WebInspector.RemoteObject.fromPrimitiveValue = function(value)
  64. {
  65. return new WebInspector.RemoteObject(undefined, typeof value, undefined, value);
  66. }
  67. /**
  68. * @param {Object} value
  69. * @return {WebInspector.RemoteObject}
  70. */
  71. WebInspector.RemoteObject.fromLocalObject = function(value)
  72. {
  73. return new WebInspector.LocalJSONObject(value);
  74. }
  75. /**
  76. * @param {WebInspector.DOMNode} node
  77. * @param {string} objectGroup
  78. * @param {function(?WebInspector.RemoteObject)} callback
  79. */
  80. WebInspector.RemoteObject.resolveNode = function(node, objectGroup, callback)
  81. {
  82. /**
  83. * @param {?Protocol.Error} error
  84. * @param {RuntimeAgent.RemoteObject} object
  85. */
  86. function mycallback(error, object)
  87. {
  88. if (!callback)
  89. return;
  90. if (error || !object)
  91. callback(null);
  92. else
  93. callback(WebInspector.RemoteObject.fromPayload(object));
  94. }
  95. DOMAgent.resolveNode(node.id, objectGroup, mycallback);
  96. }
  97. /**
  98. * @param {RuntimeAgent.RemoteObject=} payload
  99. * @return {WebInspector.RemoteObject}
  100. */
  101. WebInspector.RemoteObject.fromPayload = function(payload)
  102. {
  103. console.assert(typeof payload === "object", "Remote object payload should only be an object");
  104. return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview);
  105. }
  106. /**
  107. * @param {RuntimeAgent.RemoteObject} payload
  108. * @param {WebInspector.ScopeRef=} scopeRef
  109. * @return {WebInspector.RemoteObject}
  110. */
  111. WebInspector.RemoteObject.fromScopePayload = function(payload, scopeRef)
  112. {
  113. return new WebInspector.RemoteObject(payload.objectId, payload.type, payload.subtype, payload.value, payload.description, payload.preview, scopeRef);
  114. }
  115. /**
  116. * @param {WebInspector.RemoteObject} remoteObject
  117. * @return {string}
  118. */
  119. WebInspector.RemoteObject.type = function(remoteObject)
  120. {
  121. if (remoteObject === null)
  122. return "null";
  123. var type = typeof remoteObject;
  124. if (type !== "object" && type !== "function")
  125. return type;
  126. return remoteObject.type;
  127. }
  128. WebInspector.RemoteObject.prototype = {
  129. /** @return {RuntimeAgent.RemoteObjectId} */
  130. get objectId()
  131. {
  132. return this._objectId;
  133. },
  134. /** @return {string} */
  135. get type()
  136. {
  137. return this._type;
  138. },
  139. /** @return {string|undefined} */
  140. get subtype()
  141. {
  142. return this._subtype;
  143. },
  144. /** @return {string|undefined} */
  145. get description()
  146. {
  147. return this._description;
  148. },
  149. /** @return {boolean} */
  150. get hasChildren()
  151. {
  152. return this._hasChildren;
  153. },
  154. /** @return {RuntimeAgent.ObjectPreview|undefined} */
  155. get preview()
  156. {
  157. return this._preview;
  158. },
  159. /**
  160. * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
  161. */
  162. getOwnProperties: function(callback)
  163. {
  164. this._getProperties(true, callback);
  165. },
  166. /**
  167. * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
  168. */
  169. getAllProperties: function(callback)
  170. {
  171. this._getProperties(false, callback);
  172. },
  173. /**
  174. * @param {boolean} ownProperties
  175. * @param {function(Array.<WebInspector.RemoteObjectProperty>, Array.<WebInspector.RemoteObjectProperty>=)} callback
  176. */
  177. _getProperties: function(ownProperties, callback)
  178. {
  179. if (!this._objectId) {
  180. callback([]);
  181. return;
  182. }
  183. /**
  184. * @param {?Protocol.Error} error
  185. * @param {Array.<RuntimeAgent.PropertyDescriptor>} properties
  186. * @param {Array.<RuntimeAgent.InternalPropertyDescriptor>=} internalProperties
  187. */
  188. function remoteObjectBinder(error, properties, internalProperties)
  189. {
  190. if (error) {
  191. callback(null);
  192. return;
  193. }
  194. var result = [];
  195. for (var i = 0; properties && i < properties.length; ++i) {
  196. var property = properties[i];
  197. if (property.get || property.set) {
  198. if (property.get)
  199. result.push(new WebInspector.RemoteObjectProperty("get " + property.name, WebInspector.RemoteObject.fromPayload(property.get), property));
  200. if (property.set)
  201. result.push(new WebInspector.RemoteObjectProperty("set " + property.name, WebInspector.RemoteObject.fromPayload(property.set), property));
  202. } else
  203. result.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value), property));
  204. }
  205. var internalPropertiesResult;
  206. if (internalProperties) {
  207. internalPropertiesResult = [];
  208. for (var i = 0; i < internalProperties.length; i++) {
  209. var property = internalProperties[i];
  210. internalPropertiesResult.push(new WebInspector.RemoteObjectProperty(property.name, WebInspector.RemoteObject.fromPayload(property.value)));
  211. }
  212. }
  213. callback(result, internalPropertiesResult);
  214. }
  215. RuntimeAgent.getProperties(this._objectId, ownProperties, remoteObjectBinder);
  216. },
  217. /**
  218. * @param {string} name
  219. * @param {string} value
  220. * @param {function(string=)} callback
  221. */
  222. setPropertyValue: function(name, value, callback)
  223. {
  224. if (!this._objectId) {
  225. callback("Can't set a property of non-object.");
  226. return;
  227. }
  228. RuntimeAgent.evaluate.invoke({expression:value, doNotPauseOnExceptionsAndMuteConsole:true}, evaluatedCallback.bind(this));
  229. /**
  230. * @param {?Protocol.Error} error
  231. * @param {RuntimeAgent.RemoteObject} result
  232. * @param {boolean=} wasThrown
  233. */
  234. function evaluatedCallback(error, result, wasThrown)
  235. {
  236. if (error || wasThrown) {
  237. callback(error || result.description);
  238. return;
  239. }
  240. if (this._scopeRef)
  241. this._setDeclarativeVariableValue(result, name, callback);
  242. else
  243. this._setObjectPropertyValue(result, name, callback);
  244. if (result._objectId)
  245. RuntimeAgent.releaseObject(result._objectId);
  246. }
  247. },
  248. /**
  249. * @param {WebInspector.RemoteObject} result
  250. * @param {string} name
  251. * @param {function(string=)} callback
  252. */
  253. _setObjectPropertyValue: function(result, name, callback)
  254. {
  255. // Note that it is not that simple with accessor properties. The proto object may contain the property,
  256. // however not the proto object must be 'this', but the main object.
  257. var setPropertyValueFunction = "function(a, b) { this[a] = b; }";
  258. // Special case for NaN, Infinity and -Infinity
  259. if (result.type === "number" && typeof result.value !== "number")
  260. setPropertyValueFunction = "function(a) { this[a] = " + result.description + "; }";
  261. delete result.description; // Optimize on traffic.
  262. RuntimeAgent.callFunctionOn(this._objectId, setPropertyValueFunction, [{ value:name }, result], true, undefined, undefined, propertySetCallback.bind(this));
  263. /**
  264. * @param {?Protocol.Error} error
  265. * @param {RuntimeAgent.RemoteObject} result
  266. * @param {boolean=} wasThrown
  267. */
  268. function propertySetCallback(error, result, wasThrown)
  269. {
  270. if (error || wasThrown) {
  271. callback(error || result.description);
  272. return;
  273. }
  274. callback();
  275. }
  276. },
  277. /**
  278. * @param {WebInspector.RemoteObject} result
  279. * @param {string} name
  280. * @param {function(string=)} callback
  281. */
  282. _setDeclarativeVariableValue: function(result, name, callback)
  283. {
  284. var newValue;
  285. switch (result.type) {
  286. case "undefined":
  287. newValue = {};
  288. break;
  289. case "object":
  290. case "function":
  291. newValue = { objectId: result.objectId };
  292. break;
  293. default:
  294. newValue = { value: result.value };
  295. }
  296. DebuggerAgent.setVariableValue(this._scopeRef.number, name, newValue, this._scopeRef.callFrameId, this._scopeRef.functionId, setVariableValueCallback.bind(this));
  297. /**
  298. * @param {?Protocol.Error} error
  299. */
  300. function setVariableValueCallback(error)
  301. {
  302. if (error) {
  303. callback(error);
  304. return;
  305. }
  306. callback();
  307. }
  308. },
  309. /**
  310. * @param {function(?DOMAgent.NodeId)} callback
  311. */
  312. pushNodeToFrontend: function(callback)
  313. {
  314. if (this._objectId)
  315. WebInspector.domAgent.pushNodeToFrontend(this._objectId, callback);
  316. else
  317. callback(0);
  318. },
  319. highlightAsDOMNode: function()
  320. {
  321. WebInspector.domAgent.highlightDOMNode(undefined, undefined, this._objectId);
  322. },
  323. hideDOMNodeHighlight: function()
  324. {
  325. WebInspector.domAgent.hideDOMNodeHighlight();
  326. },
  327. /**
  328. * @param {function(this:Object)} functionDeclaration
  329. * @param {Array.<RuntimeAgent.CallArgument>=} args
  330. * @param {function(?WebInspector.RemoteObject)=} callback
  331. */
  332. callFunction: function(functionDeclaration, args, callback)
  333. {
  334. /**
  335. * @param {?Protocol.Error} error
  336. * @param {RuntimeAgent.RemoteObject} result
  337. * @param {boolean=} wasThrown
  338. */
  339. function mycallback(error, result, wasThrown)
  340. {
  341. if (!callback)
  342. return;
  343. callback((error || wasThrown) ? null : WebInspector.RemoteObject.fromPayload(result));
  344. }
  345. RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, undefined, undefined, mycallback);
  346. },
  347. /**
  348. * @param {function(this:Object)} functionDeclaration
  349. * @param {Array.<RuntimeAgent.CallArgument>|undefined} args
  350. * @param {function(*)} callback
  351. */
  352. callFunctionJSON: function(functionDeclaration, args, callback)
  353. {
  354. /**
  355. * @param {?Protocol.Error} error
  356. * @param {RuntimeAgent.RemoteObject} result
  357. * @param {boolean=} wasThrown
  358. */
  359. function mycallback(error, result, wasThrown)
  360. {
  361. callback((error || wasThrown) ? null : result.value);
  362. }
  363. RuntimeAgent.callFunctionOn(this._objectId, functionDeclaration.toString(), args, true, true, false, mycallback);
  364. },
  365. release: function()
  366. {
  367. if (!this._objectId)
  368. return;
  369. RuntimeAgent.releaseObject(this._objectId);
  370. },
  371. /**
  372. * @return {number}
  373. */
  374. arrayLength: function()
  375. {
  376. if (this.subtype !== "array")
  377. return 0;
  378. var matches = this._description.match(/\[([0-9]+)\]/);
  379. if (!matches)
  380. return 0;
  381. return parseInt(matches[1], 10);
  382. }
  383. }
  384. /**
  385. * Either callFrameId or functionId (exactly one) must be defined.
  386. * @constructor
  387. * @param {number} number
  388. * @param {string=} callFrameId
  389. * @param {string=} functionId
  390. */
  391. WebInspector.ScopeRef = function(number, callFrameId, functionId)
  392. {
  393. this.number = number;
  394. this.callFrameId = callFrameId;
  395. this.functionId = functionId;
  396. }
  397. /**
  398. * @constructor
  399. * @param {string} name
  400. * @param {WebInspector.RemoteObject} value
  401. * @param {Object=} descriptor
  402. */
  403. WebInspector.RemoteObjectProperty = function(name, value, descriptor)
  404. {
  405. this.name = name;
  406. this.value = value;
  407. this.enumerable = descriptor ? !!descriptor.enumerable : true;
  408. this.writable = descriptor ? !!descriptor.writable : true;
  409. if (descriptor && descriptor.wasThrown)
  410. this.wasThrown = true;
  411. }
  412. /**
  413. * @param {string} name
  414. * @param {string} value
  415. * @return {WebInspector.RemoteObjectProperty}
  416. */
  417. WebInspector.RemoteObjectProperty.fromPrimitiveValue = function(name, value)
  418. {
  419. return new WebInspector.RemoteObjectProperty(name, WebInspector.RemoteObject.fromPrimitiveValue(value));
  420. }
  421. /**
  422. * @param {string} name
  423. * @param {WebInspector.RemoteObject} value
  424. * @return {WebInspector.RemoteObjectProperty}
  425. */
  426. WebInspector.RemoteObjectProperty.fromScopeValue = function(name, value)
  427. {
  428. var result = new WebInspector.RemoteObjectProperty(name, value);
  429. result.writable = false;
  430. return result;
  431. }
  432. // The below is a wrapper around a local object that provides an interface comaptible
  433. // with RemoteObject, to be used by the UI code (primarily ObjectPropertiesSection).
  434. // Note that only JSON-compliant objects are currently supported, as there's no provision
  435. // for traversing prototypes, extracting class names via constuctor, handling properties
  436. // or functions.
  437. /**
  438. * @constructor
  439. * @extends {WebInspector.RemoteObject}
  440. * @param {Object} value
  441. */
  442. WebInspector.LocalJSONObject = function(value)
  443. {
  444. this._value = value;
  445. }
  446. WebInspector.LocalJSONObject.prototype = {
  447. /**
  448. * @return {string}
  449. */
  450. get description()
  451. {
  452. if (this._cachedDescription)
  453. return this._cachedDescription;
  454. if (this.type === "object") {
  455. switch (this.subtype) {
  456. case "array":
  457. function formatArrayItem(property)
  458. {
  459. return property.value.description;
  460. }
  461. this._cachedDescription = this._concatenate("[", "]", formatArrayItem);
  462. break;
  463. case "date":
  464. this._cachedDescription = "" + this._value;
  465. break;
  466. case "null":
  467. this._cachedDescription = "null";
  468. break;
  469. default:
  470. function formatObjectItem(property)
  471. {
  472. return property.name + ":" + property.value.description;
  473. }
  474. this._cachedDescription = this._concatenate("{", "}", formatObjectItem);
  475. }
  476. } else
  477. this._cachedDescription = String(this._value);
  478. return this._cachedDescription;
  479. },
  480. /**
  481. * @param {string} prefix
  482. * @param {string} suffix
  483. * @return {string}
  484. */
  485. _concatenate: function(prefix, suffix, formatProperty)
  486. {
  487. const previewChars = 100;
  488. var buffer = prefix;
  489. var children = this._children();
  490. for (var i = 0; i < children.length; ++i) {
  491. var itemDescription = formatProperty(children[i]);
  492. if (buffer.length + itemDescription.length > previewChars) {
  493. buffer += ",\u2026";
  494. break;
  495. }
  496. if (i)
  497. buffer += ", ";
  498. buffer += itemDescription;
  499. }
  500. buffer += suffix;
  501. return buffer;
  502. },
  503. /**
  504. * @return {string}
  505. */
  506. get type()
  507. {
  508. return typeof this._value;
  509. },
  510. /**
  511. * @return {string|undefined}
  512. */
  513. get subtype()
  514. {
  515. if (this._value === null)
  516. return "null";
  517. if (this._value instanceof Array)
  518. return "array";
  519. if (this._value instanceof Date)
  520. return "date";
  521. return undefined;
  522. },
  523. /**
  524. * @return {boolean}
  525. */
  526. get hasChildren()
  527. {
  528. return typeof this._value === "object" && this._value !== null && !!Object.keys(this._value).length;
  529. },
  530. /**
  531. * @param {function(Array.<WebInspector.RemoteObjectProperty>)} callback
  532. */
  533. getOwnProperties: function(callback)
  534. {
  535. callback(this._children());
  536. },
  537. /**
  538. * @param {function(Array.<WebInspector.RemoteObjectProperty>)} callback
  539. */
  540. getAllProperties: function(callback)
  541. {
  542. callback(this._children());
  543. },
  544. /**
  545. * @return {Array.<WebInspector.RemoteObjectProperty>}
  546. */
  547. _children: function()
  548. {
  549. if (!this.hasChildren)
  550. return [];
  551. function buildProperty(propName)
  552. {
  553. return new WebInspector.RemoteObjectProperty(propName, new WebInspector.LocalJSONObject(this._value[propName]));
  554. }
  555. if (!this._cachedChildren)
  556. this._cachedChildren = Object.keys(this._value || {}).map(buildProperty.bind(this));
  557. return this._cachedChildren;
  558. },
  559. /**
  560. * @return {boolean}
  561. */
  562. isError: function()
  563. {
  564. return false;
  565. },
  566. /**
  567. * @return {number}
  568. */
  569. arrayLength: function()
  570. {
  571. return this._value instanceof Array ? this._value.length : 0;
  572. }
  573. }