object.js 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. "use strict";
  6. const { Cu, Ci } = require("chrome");
  7. const { GeneratedLocation } = require("devtools/server/actors/common");
  8. const { DebuggerServer } = require("devtools/server/main");
  9. const DevToolsUtils = require("devtools/shared/DevToolsUtils");
  10. const { assert, dumpn } = DevToolsUtils;
  11. loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
  12. const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
  13. "Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
  14. "Float64Array"];
  15. // Number of items to preview in objects, arrays, maps, sets, lists,
  16. // collections, etc.
  17. const OBJECT_PREVIEW_MAX_ITEMS = 10;
  18. /**
  19. * Creates an actor for the specified object.
  20. *
  21. * @param obj Debugger.Object
  22. * The debuggee object.
  23. * @param hooks Object
  24. * A collection of abstract methods that are implemented by the caller.
  25. * ObjectActor requires the following functions to be implemented by
  26. * the caller:
  27. * - createValueGrip
  28. * Creates a value grip for the given object
  29. * - sources
  30. * TabSources getter that manages the sources of a thread
  31. * - createEnvironmentActor
  32. * Creates and return an environment actor
  33. * - getGripDepth
  34. * An actor's grip depth getter
  35. * - incrementGripDepth
  36. * Increment the actor's grip depth
  37. * - decrementGripDepth
  38. * Decrement the actor's grip depth
  39. * - globalDebugObject
  40. * The Debuggee Global Object as given by the ThreadActor
  41. */
  42. function ObjectActor(obj, {
  43. createValueGrip,
  44. sources,
  45. createEnvironmentActor,
  46. getGripDepth,
  47. incrementGripDepth,
  48. decrementGripDepth,
  49. getGlobalDebugObject
  50. }) {
  51. assert(!obj.optimizedOut,
  52. "Should not create object actors for optimized out values!");
  53. this.obj = obj;
  54. this.hooks = {
  55. createValueGrip,
  56. sources,
  57. createEnvironmentActor,
  58. getGripDepth,
  59. incrementGripDepth,
  60. decrementGripDepth,
  61. getGlobalDebugObject
  62. };
  63. this.iterators = new Set();
  64. }
  65. ObjectActor.prototype = {
  66. actorPrefix: "obj",
  67. /**
  68. * Returns a grip for this actor for returning in a protocol message.
  69. */
  70. grip: function () {
  71. this.hooks.incrementGripDepth();
  72. let g = {
  73. "type": "object",
  74. "actor": this.actorID
  75. };
  76. // If it's a proxy, lie and tell that it belongs to an invented
  77. // "Proxy" class, and avoid calling the [[IsExtensible]] trap
  78. if(this.obj.isProxy) {
  79. g.class = "Proxy";
  80. g.proxyTarget = this.hooks.createValueGrip(this.obj.proxyTarget);
  81. g.proxyHandler = this.hooks.createValueGrip(this.obj.proxyHandler);
  82. } else {
  83. g.class = this.obj.class;
  84. g.extensible = this.obj.isExtensible();
  85. g.frozen = this.obj.isFrozen();
  86. g.sealed = this.obj.isSealed();
  87. }
  88. if (g.class != "DeadObject") {
  89. if (g.class == "Promise") {
  90. g.promiseState = this._createPromiseState();
  91. }
  92. // FF40+: Allow to know how many properties an object has
  93. // to lazily display them when there is a bunch.
  94. // Throws on some MouseEvent object in tests.
  95. try {
  96. // Bug 1163520: Assert on internal functions
  97. if (!["Function", "Proxy"].includes(g.class)) {
  98. g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
  99. }
  100. } catch (e) {}
  101. let raw = this.obj.unsafeDereference();
  102. // If Cu is not defined, we are running on a worker thread, where xrays
  103. // don't exist.
  104. if (Cu) {
  105. raw = Cu.unwaiveXrays(raw);
  106. }
  107. if (!DevToolsUtils.isSafeJSObject(raw)) {
  108. raw = null;
  109. }
  110. let previewers = DebuggerServer.ObjectActorPreviewers[g.class] ||
  111. DebuggerServer.ObjectActorPreviewers.Object;
  112. for (let fn of previewers) {
  113. try {
  114. if (fn(this, g, raw)) {
  115. break;
  116. }
  117. } catch (e) {
  118. let msg = "ObjectActor.prototype.grip previewer function";
  119. DevToolsUtils.reportException(msg, e);
  120. }
  121. }
  122. }
  123. this.hooks.decrementGripDepth();
  124. return g;
  125. },
  126. /**
  127. * Returns an object exposing the internal Promise state.
  128. */
  129. _createPromiseState: function () {
  130. const { state, value, reason } = getPromiseState(this.obj);
  131. let promiseState = { state };
  132. if (state == "fulfilled") {
  133. promiseState.value = this.hooks.createValueGrip(value);
  134. } else if (state == "rejected") {
  135. promiseState.reason = this.hooks.createValueGrip(reason);
  136. }
  137. promiseState.creationTimestamp = Date.now() - this.obj.promiseLifetime;
  138. // Only add the timeToSettle property if the Promise isn't pending.
  139. if (state !== "pending") {
  140. promiseState.timeToSettle = this.obj.promiseTimeToResolution;
  141. }
  142. return promiseState;
  143. },
  144. /**
  145. * Releases this actor from the pool.
  146. */
  147. release: function () {
  148. if (this.registeredPool.objectActors) {
  149. this.registeredPool.objectActors.delete(this.obj);
  150. }
  151. this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
  152. this.iterators.clear();
  153. this.registeredPool.removeActor(this);
  154. },
  155. /**
  156. * Handle a protocol request to provide the definition site of this function
  157. * object.
  158. */
  159. onDefinitionSite: function () {
  160. if (this.obj.class != "Function") {
  161. return {
  162. from: this.actorID,
  163. error: "objectNotFunction",
  164. message: this.actorID + " is not a function."
  165. };
  166. }
  167. if (!this.obj.script) {
  168. return {
  169. from: this.actorID,
  170. error: "noScript",
  171. message: this.actorID + " has no Debugger.Script"
  172. };
  173. }
  174. return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
  175. this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
  176. this.obj.script.startLine,
  177. 0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
  178. )).then((originalLocation) => {
  179. return {
  180. source: originalLocation.originalSourceActor.form(),
  181. line: originalLocation.originalLine,
  182. column: originalLocation.originalColumn
  183. };
  184. });
  185. },
  186. /**
  187. * Handle a protocol request to provide the names of the properties defined on
  188. * the object and not its prototype.
  189. */
  190. onOwnPropertyNames: function () {
  191. return { from: this.actorID,
  192. ownPropertyNames: this.obj.getOwnPropertyNames() };
  193. },
  194. /**
  195. * Creates an actor to iterate over an object property names and values.
  196. * See PropertyIteratorActor constructor for more info about options param.
  197. *
  198. * @param request object
  199. * The protocol request object.
  200. */
  201. onEnumProperties: function (request) {
  202. let actor = new PropertyIteratorActor(this, request.options);
  203. this.registeredPool.addActor(actor);
  204. this.iterators.add(actor);
  205. return { iterator: actor.grip() };
  206. },
  207. /**
  208. * Creates an actor to iterate over entries of a Map/Set-like object.
  209. */
  210. onEnumEntries: function () {
  211. let actor = new PropertyIteratorActor(this, { enumEntries: true });
  212. this.registeredPool.addActor(actor);
  213. this.iterators.add(actor);
  214. return { iterator: actor.grip() };
  215. },
  216. /**
  217. * Handle a protocol request to provide the prototype and own properties of
  218. * the object.
  219. */
  220. onPrototypeAndProperties: function () {
  221. let ownProperties = Object.create(null);
  222. let names;
  223. try {
  224. names = this.obj.getOwnPropertyNames();
  225. } catch (ex) {
  226. // The above can throw if this.obj points to a dead object.
  227. // TODO: we should use Cu.isDeadWrapper() - see bug 885800.
  228. return { from: this.actorID,
  229. prototype: this.hooks.createValueGrip(null),
  230. ownProperties: ownProperties,
  231. safeGetterValues: Object.create(null) };
  232. }
  233. for (let name of names) {
  234. ownProperties[name] = this._propertyDescriptor(name);
  235. }
  236. return { from: this.actorID,
  237. prototype: this.hooks.createValueGrip(this.obj.proto),
  238. ownProperties: ownProperties,
  239. safeGetterValues: this._findSafeGetterValues(names) };
  240. },
  241. /**
  242. * Find the safe getter values for the current Debugger.Object, |this.obj|.
  243. *
  244. * @private
  245. * @param array ownProperties
  246. * The array that holds the list of known ownProperties names for
  247. * |this.obj|.
  248. * @param number [limit=0]
  249. * Optional limit of getter values to find.
  250. * @return object
  251. * An object that maps property names to safe getter descriptors as
  252. * defined by the remote debugging protocol.
  253. */
  254. _findSafeGetterValues: function (ownProperties, limit = 0) {
  255. let safeGetterValues = Object.create(null);
  256. let obj = this.obj;
  257. let level = 0, i = 0;
  258. // Most objects don't have any safe getters but inherit some from their
  259. // prototype. Avoid calling getOwnPropertyNames on objects that may have
  260. // many properties like Array, strings or js objects. That to avoid
  261. // freezing firefox when doing so.
  262. if (TYPED_ARRAY_CLASSES.includes(this.obj.class) ||
  263. ["Array", "Object", "String"].includes(this.obj.class)) {
  264. obj = obj.proto;
  265. level++;
  266. }
  267. while (obj) {
  268. let getters = this._findSafeGetters(obj);
  269. for (let name of getters) {
  270. // Avoid overwriting properties from prototypes closer to this.obj. Also
  271. // avoid providing safeGetterValues from prototypes if property |name|
  272. // is already defined as an own property.
  273. if (name in safeGetterValues ||
  274. (obj != this.obj && ownProperties.indexOf(name) !== -1)) {
  275. continue;
  276. }
  277. // Ignore __proto__ on Object.prototye.
  278. if (!obj.proto && name == "__proto__") {
  279. continue;
  280. }
  281. let desc = null, getter = null;
  282. try {
  283. desc = obj.getOwnPropertyDescriptor(name);
  284. getter = desc.get;
  285. } catch (ex) {
  286. // The above can throw if the cache becomes stale.
  287. }
  288. if (!getter) {
  289. obj._safeGetters = null;
  290. continue;
  291. }
  292. let result = getter.call(this.obj);
  293. if (result && !("throw" in result)) {
  294. let getterValue = undefined;
  295. if ("return" in result) {
  296. getterValue = result.return;
  297. } else if ("yield" in result) {
  298. getterValue = result.yield;
  299. }
  300. // WebIDL attributes specified with the LenientThis extended attribute
  301. // return undefined and should be ignored.
  302. if (getterValue !== undefined) {
  303. safeGetterValues[name] = {
  304. getterValue: this.hooks.createValueGrip(getterValue),
  305. getterPrototypeLevel: level,
  306. enumerable: desc.enumerable,
  307. writable: level == 0 ? desc.writable : true,
  308. };
  309. if (limit && ++i == limit) {
  310. break;
  311. }
  312. }
  313. }
  314. }
  315. if (limit && i == limit) {
  316. break;
  317. }
  318. obj = obj.proto;
  319. level++;
  320. }
  321. return safeGetterValues;
  322. },
  323. /**
  324. * Find the safe getters for a given Debugger.Object. Safe getters are native
  325. * getters which are safe to execute.
  326. *
  327. * @private
  328. * @param Debugger.Object object
  329. * The Debugger.Object where you want to find safe getters.
  330. * @return Set
  331. * A Set of names of safe getters. This result is cached for each
  332. * Debugger.Object.
  333. */
  334. _findSafeGetters: function (object) {
  335. if (object._safeGetters) {
  336. return object._safeGetters;
  337. }
  338. let getters = new Set();
  339. let names = [];
  340. try {
  341. names = object.getOwnPropertyNames();
  342. } catch (ex) {
  343. // Calling getOwnPropertyNames() on some wrapped native prototypes is not
  344. // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
  345. }
  346. for (let name of names) {
  347. let desc = null;
  348. try {
  349. desc = object.getOwnPropertyDescriptor(name);
  350. } catch (e) {
  351. // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
  352. // allowed (bug 560072).
  353. }
  354. if (!desc || desc.value !== undefined || !("get" in desc)) {
  355. continue;
  356. }
  357. if (DevToolsUtils.hasSafeGetter(desc)) {
  358. getters.add(name);
  359. }
  360. }
  361. object._safeGetters = getters;
  362. return getters;
  363. },
  364. /**
  365. * Handle a protocol request to provide the prototype of the object.
  366. */
  367. onPrototype: function () {
  368. return { from: this.actorID,
  369. prototype: this.hooks.createValueGrip(this.obj.proto) };
  370. },
  371. /**
  372. * Handle a protocol request to provide the property descriptor of the
  373. * object's specified property.
  374. *
  375. * @param request object
  376. * The protocol request object.
  377. */
  378. onProperty: function (request) {
  379. if (!request.name) {
  380. return { error: "missingParameter",
  381. message: "no property name was specified" };
  382. }
  383. return { from: this.actorID,
  384. descriptor: this._propertyDescriptor(request.name) };
  385. },
  386. /**
  387. * Handle a protocol request to provide the display string for the object.
  388. */
  389. onDisplayString: function () {
  390. const string = stringify(this.obj);
  391. return { from: this.actorID,
  392. displayString: this.hooks.createValueGrip(string) };
  393. },
  394. /**
  395. * A helper method that creates a property descriptor for the provided object,
  396. * properly formatted for sending in a protocol response.
  397. *
  398. * @private
  399. * @param string name
  400. * The property that the descriptor is generated for.
  401. * @param boolean [onlyEnumerable]
  402. * Optional: true if you want a descriptor only for an enumerable
  403. * property, false otherwise.
  404. * @return object|undefined
  405. * The property descriptor, or undefined if this is not an enumerable
  406. * property and onlyEnumerable=true.
  407. */
  408. _propertyDescriptor: function (name, onlyEnumerable) {
  409. let desc;
  410. try {
  411. desc = this.obj.getOwnPropertyDescriptor(name);
  412. } catch (e) {
  413. // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
  414. // allowed (bug 560072). Inform the user with a bogus, but hopefully
  415. // explanatory, descriptor.
  416. return {
  417. configurable: false,
  418. writable: false,
  419. enumerable: false,
  420. value: e.name
  421. };
  422. }
  423. if (!desc || onlyEnumerable && !desc.enumerable) {
  424. return undefined;
  425. }
  426. let retval = {
  427. configurable: desc.configurable,
  428. enumerable: desc.enumerable
  429. };
  430. if ("value" in desc) {
  431. retval.writable = desc.writable;
  432. retval.value = this.hooks.createValueGrip(desc.value);
  433. } else {
  434. if ("get" in desc) {
  435. retval.get = this.hooks.createValueGrip(desc.get);
  436. }
  437. if ("set" in desc) {
  438. retval.set = this.hooks.createValueGrip(desc.set);
  439. }
  440. }
  441. return retval;
  442. },
  443. /**
  444. * Handle a protocol request to provide the source code of a function.
  445. *
  446. * @param request object
  447. * The protocol request object.
  448. */
  449. onDecompile: function (request) {
  450. if (this.obj.class !== "Function") {
  451. return { error: "objectNotFunction",
  452. message: "decompile request is only valid for object grips " +
  453. "with a 'Function' class." };
  454. }
  455. return { from: this.actorID,
  456. decompiledCode: this.obj.decompile(!!request.pretty) };
  457. },
  458. /**
  459. * Handle a protocol request to provide the parameters of a function.
  460. */
  461. onParameterNames: function () {
  462. if (this.obj.class !== "Function") {
  463. return { error: "objectNotFunction",
  464. message: "'parameterNames' request is only valid for object " +
  465. "grips with a 'Function' class." };
  466. }
  467. return { parameterNames: this.obj.parameterNames };
  468. },
  469. /**
  470. * Handle a protocol request to release a thread-lifetime grip.
  471. */
  472. onRelease: function () {
  473. this.release();
  474. return {};
  475. },
  476. /**
  477. * Handle a protocol request to provide the lexical scope of a function.
  478. */
  479. onScope: function () {
  480. if (this.obj.class !== "Function") {
  481. return { error: "objectNotFunction",
  482. message: "scope request is only valid for object grips with a" +
  483. " 'Function' class." };
  484. }
  485. let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
  486. this.registeredPool);
  487. if (!envActor) {
  488. return { error: "notDebuggee",
  489. message: "cannot access the environment of this function." };
  490. }
  491. return { from: this.actorID, scope: envActor.form() };
  492. },
  493. /**
  494. * Handle a protocol request to get the list of dependent promises of a
  495. * promise.
  496. *
  497. * @return object
  498. * Returns an object containing an array of object grips of the
  499. * dependent promises
  500. */
  501. onDependentPromises: function () {
  502. if (this.obj.class != "Promise") {
  503. return { error: "objectNotPromise",
  504. message: "'dependentPromises' request is only valid for " +
  505. "object grips with a 'Promise' class." };
  506. }
  507. let promises = this.obj.promiseDependentPromises.map(p => this.hooks.createValueGrip(p));
  508. return { promises };
  509. },
  510. /**
  511. * Handle a protocol request to get the allocation stack of a promise.
  512. */
  513. onAllocationStack: function () {
  514. if (this.obj.class != "Promise") {
  515. return { error: "objectNotPromise",
  516. message: "'allocationStack' request is only valid for " +
  517. "object grips with a 'Promise' class." };
  518. }
  519. let stack = this.obj.promiseAllocationSite;
  520. let allocationStacks = [];
  521. while (stack) {
  522. if (stack.source) {
  523. let source = this._getSourceOriginalLocation(stack);
  524. if (source) {
  525. allocationStacks.push(source);
  526. }
  527. }
  528. stack = stack.parent;
  529. }
  530. return Promise.all(allocationStacks).then(stacks => {
  531. return { allocationStack: stacks };
  532. });
  533. },
  534. /**
  535. * Handle a protocol request to get the fulfillment stack of a promise.
  536. */
  537. onFulfillmentStack: function () {
  538. if (this.obj.class != "Promise") {
  539. return { error: "objectNotPromise",
  540. message: "'fulfillmentStack' request is only valid for " +
  541. "object grips with a 'Promise' class." };
  542. }
  543. let stack = this.obj.promiseResolutionSite;
  544. let fulfillmentStacks = [];
  545. while (stack) {
  546. if (stack.source) {
  547. let source = this._getSourceOriginalLocation(stack);
  548. if (source) {
  549. fulfillmentStacks.push(source);
  550. }
  551. }
  552. stack = stack.parent;
  553. }
  554. return Promise.all(fulfillmentStacks).then(stacks => {
  555. return { fulfillmentStack: stacks };
  556. });
  557. },
  558. /**
  559. * Handle a protocol request to get the rejection stack of a promise.
  560. */
  561. onRejectionStack: function () {
  562. if (this.obj.class != "Promise") {
  563. return { error: "objectNotPromise",
  564. message: "'rejectionStack' request is only valid for " +
  565. "object grips with a 'Promise' class." };
  566. }
  567. let stack = this.obj.promiseResolutionSite;
  568. let rejectionStacks = [];
  569. while (stack) {
  570. if (stack.source) {
  571. let source = this._getSourceOriginalLocation(stack);
  572. if (source) {
  573. rejectionStacks.push(source);
  574. }
  575. }
  576. stack = stack.parent;
  577. }
  578. return Promise.all(rejectionStacks).then(stacks => {
  579. return { rejectionStack: stacks };
  580. });
  581. },
  582. /**
  583. * Helper function for fetching the source location of a SavedFrame stack.
  584. *
  585. * @param SavedFrame stack
  586. * The promise allocation stack frame
  587. * @return object
  588. * Returns an object containing the source location of the SavedFrame
  589. * stack.
  590. */
  591. _getSourceOriginalLocation: function (stack) {
  592. let source;
  593. // Catch any errors if the source actor cannot be found
  594. try {
  595. source = this.hooks.sources().getSourceActorByURL(stack.source);
  596. } catch (e) {}
  597. if (!source) {
  598. return null;
  599. }
  600. return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
  601. source,
  602. stack.line,
  603. stack.column
  604. )).then((originalLocation) => {
  605. return {
  606. source: originalLocation.originalSourceActor.form(),
  607. line: originalLocation.originalLine,
  608. column: originalLocation.originalColumn,
  609. functionDisplayName: stack.functionDisplayName
  610. };
  611. });
  612. }
  613. };
  614. ObjectActor.prototype.requestTypes = {
  615. "definitionSite": ObjectActor.prototype.onDefinitionSite,
  616. "parameterNames": ObjectActor.prototype.onParameterNames,
  617. "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
  618. "enumProperties": ObjectActor.prototype.onEnumProperties,
  619. "prototype": ObjectActor.prototype.onPrototype,
  620. "property": ObjectActor.prototype.onProperty,
  621. "displayString": ObjectActor.prototype.onDisplayString,
  622. "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
  623. "decompile": ObjectActor.prototype.onDecompile,
  624. "release": ObjectActor.prototype.onRelease,
  625. "scope": ObjectActor.prototype.onScope,
  626. "dependentPromises": ObjectActor.prototype.onDependentPromises,
  627. "allocationStack": ObjectActor.prototype.onAllocationStack,
  628. "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
  629. "rejectionStack": ObjectActor.prototype.onRejectionStack,
  630. "enumEntries": ObjectActor.prototype.onEnumEntries,
  631. };
  632. /**
  633. * Creates an actor to iterate over an object's property names and values.
  634. *
  635. * @param objectActor ObjectActor
  636. * The object actor.
  637. * @param options Object
  638. * A dictionary object with various boolean attributes:
  639. * - enumEntries Boolean
  640. * If true, enumerates the entries of a Map or Set object
  641. * instead of enumerating properties.
  642. * - ignoreIndexedProperties Boolean
  643. * If true, filters out Array items.
  644. * e.g. properties names between `0` and `object.length`.
  645. * - ignoreNonIndexedProperties Boolean
  646. * If true, filters out items that aren't array items
  647. * e.g. properties names that are not a number between `0`
  648. * and `object.length`.
  649. * - sort Boolean
  650. * If true, the iterator will sort the properties by name
  651. * before dispatching them.
  652. * - query String
  653. * If non-empty, will filter the properties by names and values
  654. * containing this query string. The match is not case-sensitive.
  655. * Regarding value filtering it just compare to the stringification
  656. * of the property value.
  657. */
  658. function PropertyIteratorActor(objectActor, options) {
  659. if (options.enumEntries) {
  660. let cls = objectActor.obj.class;
  661. if (cls == "Map") {
  662. this.iterator = enumMapEntries(objectActor);
  663. } else if (cls == "WeakMap") {
  664. this.iterator = enumWeakMapEntries(objectActor);
  665. } else if (cls == "Set") {
  666. this.iterator = enumSetEntries(objectActor);
  667. } else if (cls == "WeakSet") {
  668. this.iterator = enumWeakSetEntries(objectActor);
  669. } else {
  670. throw new Error("Unsupported class to enumerate entries from: " + cls);
  671. }
  672. } else if (options.ignoreNonIndexedProperties && !options.query) {
  673. this.iterator = enumArrayProperties(objectActor, options);
  674. } else {
  675. this.iterator = enumObjectProperties(objectActor, options);
  676. }
  677. }
  678. PropertyIteratorActor.prototype = {
  679. actorPrefix: "propertyIterator",
  680. grip() {
  681. return {
  682. type: this.actorPrefix,
  683. actor: this.actorID,
  684. count: this.iterator.size
  685. };
  686. },
  687. names({ indexes }) {
  688. let list = [];
  689. for (let idx of indexes) {
  690. list.push(this.iterator.propertyName(idx));
  691. }
  692. return {
  693. names: indexes
  694. };
  695. },
  696. slice({ start, count }) {
  697. let ownProperties = Object.create(null);
  698. for (let i = start, m = start + count; i < m; i++) {
  699. let name = this.iterator.propertyName(i);
  700. ownProperties[name] = this.iterator.propertyDescription(i);
  701. }
  702. return {
  703. ownProperties
  704. };
  705. },
  706. all() {
  707. return this.slice({ start: 0, count: this.length });
  708. }
  709. };
  710. PropertyIteratorActor.prototype.requestTypes = {
  711. "names": PropertyIteratorActor.prototype.names,
  712. "slice": PropertyIteratorActor.prototype.slice,
  713. "all": PropertyIteratorActor.prototype.all,
  714. };
  715. function enumArrayProperties(objectActor, options) {
  716. let length = DevToolsUtils.getProperty(objectActor.obj, "length");
  717. if (typeof length !== "number") {
  718. // Pseudo arrays are flagged as ArrayLike if they have
  719. // subsequent indexed properties without having any length attribute.
  720. length = 0;
  721. let names = objectActor.obj.getOwnPropertyNames();
  722. for (let key of names) {
  723. if (isNaN(key) || key != length++) {
  724. break;
  725. }
  726. }
  727. }
  728. return {
  729. size: length,
  730. propertyName(index) {
  731. return index;
  732. },
  733. propertyDescription(index) {
  734. return objectActor._propertyDescriptor(index);
  735. }
  736. };
  737. }
  738. function enumObjectProperties(objectActor, options) {
  739. let names = [];
  740. try {
  741. names = objectActor.obj.getOwnPropertyNames();
  742. } catch (ex) {
  743. // Calling getOwnPropertyNames() on some wrapped native prototypes is not
  744. // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
  745. }
  746. if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
  747. let length = DevToolsUtils.getProperty(objectActor.obj, "length");
  748. if (typeof length !== "number") {
  749. // Pseudo arrays are flagged as ArrayLike if they have
  750. // subsequent indexed properties without having any length attribute.
  751. length = 0;
  752. for (let key of names) {
  753. if (isNaN(key) || key != length++) {
  754. break;
  755. }
  756. }
  757. }
  758. // It appears that getOwnPropertyNames always returns indexed properties
  759. // first, so we can safely slice `names` for/against indexed properties.
  760. // We do such clever operation to optimize very large array inspection,
  761. // like webaudio buffers.
  762. if (options.ignoreIndexedProperties) {
  763. // Keep items after `length` index
  764. names = names.slice(length);
  765. } else if (options.ignoreNonIndexedProperties) {
  766. // Remove `length` first items
  767. names.splice(length);
  768. }
  769. }
  770. let safeGetterValues = objectActor._findSafeGetterValues(names, 0);
  771. let safeGetterNames = Object.keys(safeGetterValues);
  772. // Merge the safe getter values into the existing properties list.
  773. for (let name of safeGetterNames) {
  774. if (!names.includes(name)) {
  775. names.push(name);
  776. }
  777. }
  778. if (options.query) {
  779. let { query } = options;
  780. query = query.toLowerCase();
  781. names = names.filter(name => {
  782. // Filter on attribute names
  783. if (name.toLowerCase().includes(query)) {
  784. return true;
  785. }
  786. // and then on attribute values
  787. let desc;
  788. try {
  789. desc = objectActor.obj.getOwnPropertyDescriptor(name);
  790. } catch (e) {
  791. // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
  792. // allowed (bug 560072).
  793. }
  794. if (desc && desc.value &&
  795. String(desc.value).includes(query)) {
  796. return true;
  797. }
  798. return false;
  799. });
  800. }
  801. if (options.sort) {
  802. names.sort();
  803. }
  804. return {
  805. size: names.length,
  806. propertyName(index) {
  807. return names[index];
  808. },
  809. propertyDescription(index) {
  810. let name = names[index];
  811. let desc = objectActor._propertyDescriptor(name);
  812. if (!desc) {
  813. desc = safeGetterValues[name];
  814. } else if (name in safeGetterValues) {
  815. // Merge the safe getter values into the existing properties list.
  816. let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
  817. desc.getterValue = getterValue;
  818. desc.getterPrototypeLevel = getterPrototypeLevel;
  819. }
  820. return desc;
  821. }
  822. };
  823. }
  824. /**
  825. * Helper function to create a grip from a Map/Set entry
  826. */
  827. function gripFromEntry({ obj, hooks }, entry) {
  828. return hooks.createValueGrip(
  829. makeDebuggeeValueIfNeeded(obj, Cu.unwaiveXrays(entry)));
  830. }
  831. function enumMapEntries(objectActor) {
  832. // Iterating over a Map via .entries goes through various intermediate
  833. // objects - an Iterator object, then a 2-element Array object, then the
  834. // actual values we care about. We don't have Xrays to Iterator objects,
  835. // so we get Opaque wrappers for them. And even though we have Xrays to
  836. // Arrays, the semantics often deny access to the entires based on the
  837. // nature of the values. So we need waive Xrays for the iterator object
  838. // and the tupes, and then re-apply them on the underlying values until
  839. // we fix bug 1023984.
  840. //
  841. // Even then though, we might want to continue waiving Xrays here for the
  842. // same reason we do so for Arrays above - this filtering behavior is likely
  843. // to be more confusing than beneficial in the case of Object previews.
  844. let raw = objectActor.obj.unsafeDereference();
  845. let keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
  846. return {
  847. [Symbol.iterator]: function* () {
  848. for (let key of keys) {
  849. let value = Map.prototype.get.call(raw, key);
  850. yield [ key, value ].map(val => gripFromEntry(objectActor, val));
  851. }
  852. },
  853. size: keys.length,
  854. propertyName(index) {
  855. return index;
  856. },
  857. propertyDescription(index) {
  858. let key = keys[index];
  859. let val = Map.prototype.get.call(raw, key);
  860. return {
  861. enumerable: true,
  862. value: {
  863. type: "mapEntry",
  864. preview: {
  865. key: gripFromEntry(objectActor, key),
  866. value: gripFromEntry(objectActor, val)
  867. }
  868. }
  869. };
  870. }
  871. };
  872. }
  873. function enumWeakMapEntries(objectActor) {
  874. // We currently lack XrayWrappers for WeakMap, so when we iterate over
  875. // the values, the temporary iterator objects get created in the target
  876. // compartment. However, we _do_ have Xrays to Object now, so we end up
  877. // Xraying those temporary objects, and filtering access to |it.value|
  878. // based on whether or not it's Xrayable and/or callable, which breaks
  879. // the for/of iteration.
  880. //
  881. // This code is designed to handle untrusted objects, so we can safely
  882. // waive Xrays on the iterable, and relying on the Debugger machinery to
  883. // make sure we handle the resulting objects carefully.
  884. let raw = objectActor.obj.unsafeDereference();
  885. let keys = Cu.waiveXrays(
  886. ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(raw));
  887. return {
  888. [Symbol.iterator]: function* () {
  889. for (let key of keys) {
  890. let value = WeakMap.prototype.get.call(raw, key);
  891. yield [ key, value ].map(val => gripFromEntry(objectActor, val));
  892. }
  893. },
  894. size: keys.length,
  895. propertyName(index) {
  896. return index;
  897. },
  898. propertyDescription(index) {
  899. let key = keys[index];
  900. let val = WeakMap.prototype.get.call(raw, key);
  901. return {
  902. enumerable: true,
  903. value: {
  904. type: "mapEntry",
  905. preview: {
  906. key: gripFromEntry(objectActor, key),
  907. value: gripFromEntry(objectActor, val)
  908. }
  909. }
  910. };
  911. }
  912. };
  913. }
  914. function enumSetEntries(objectActor) {
  915. // We currently lack XrayWrappers for Set, so when we iterate over
  916. // the values, the temporary iterator objects get created in the target
  917. // compartment. However, we _do_ have Xrays to Object now, so we end up
  918. // Xraying those temporary objects, and filtering access to |it.value|
  919. // based on whether or not it's Xrayable and/or callable, which breaks
  920. // the for/of iteration.
  921. //
  922. // This code is designed to handle untrusted objects, so we can safely
  923. // waive Xrays on the iterable, and relying on the Debugger machinery to
  924. // make sure we handle the resulting objects carefully.
  925. let raw = objectActor.obj.unsafeDereference();
  926. let values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];
  927. return {
  928. [Symbol.iterator]: function* () {
  929. for (let item of values) {
  930. yield gripFromEntry(objectActor, item);
  931. }
  932. },
  933. size: values.length,
  934. propertyName(index) {
  935. return index;
  936. },
  937. propertyDescription(index) {
  938. let val = values[index];
  939. return {
  940. enumerable: true,
  941. value: gripFromEntry(objectActor, val)
  942. };
  943. }
  944. };
  945. }
  946. function enumWeakSetEntries(objectActor) {
  947. // We currently lack XrayWrappers for WeakSet, so when we iterate over
  948. // the values, the temporary iterator objects get created in the target
  949. // compartment. However, we _do_ have Xrays to Object now, so we end up
  950. // Xraying those temporary objects, and filtering access to |it.value|
  951. // based on whether or not it's Xrayable and/or callable, which breaks
  952. // the for/of iteration.
  953. //
  954. // This code is designed to handle untrusted objects, so we can safely
  955. // waive Xrays on the iterable, and relying on the Debugger machinery to
  956. // make sure we handle the resulting objects carefully.
  957. let raw = objectActor.obj.unsafeDereference();
  958. let keys = Cu.waiveXrays(
  959. ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(raw));
  960. return {
  961. [Symbol.iterator]: function* () {
  962. for (let item of keys) {
  963. yield gripFromEntry(objectActor, item);
  964. }
  965. },
  966. size: keys.length,
  967. propertyName(index) {
  968. return index;
  969. },
  970. propertyDescription(index) {
  971. let val = keys[index];
  972. return {
  973. enumerable: true,
  974. value: gripFromEntry(objectActor, val)
  975. };
  976. }
  977. };
  978. }
  979. /**
  980. * Functions for adding information to ObjectActor grips for the purpose of
  981. * having customized output. This object holds arrays mapped by
  982. * Debugger.Object.prototype.class.
  983. *
  984. * In each array you can add functions that take three
  985. * arguments:
  986. * - the ObjectActor instance and its hooks to make a preview for,
  987. * - the grip object being prepared for the client,
  988. * - the raw JS object after calling Debugger.Object.unsafeDereference(). This
  989. * argument is only provided if the object is safe for reading properties and
  990. * executing methods. See DevToolsUtils.isSafeJSObject().
  991. *
  992. * Functions must return false if they cannot provide preview
  993. * information for the debugger object, or true otherwise.
  994. */
  995. DebuggerServer.ObjectActorPreviewers = {
  996. String: [function (objectActor, grip, rawObj) {
  997. return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
  998. }],
  999. Boolean: [function (objectActor, grip, rawObj) {
  1000. return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
  1001. }],
  1002. Number: [function (objectActor, grip, rawObj) {
  1003. return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
  1004. }],
  1005. Function: [function ({obj, hooks}, grip) {
  1006. if (obj.name) {
  1007. grip.name = obj.name;
  1008. }
  1009. if (obj.displayName) {
  1010. grip.displayName = obj.displayName.substr(0, 500);
  1011. }
  1012. if (obj.parameterNames) {
  1013. grip.parameterNames = obj.parameterNames;
  1014. }
  1015. // Check if the developer has added a de-facto standard displayName
  1016. // property for us to use.
  1017. let userDisplayName;
  1018. try {
  1019. userDisplayName = obj.getOwnPropertyDescriptor("displayName");
  1020. } catch (e) {
  1021. // Calling getOwnPropertyDescriptor with displayName might throw
  1022. // with "permission denied" errors for some functions.
  1023. dumpn(e);
  1024. }
  1025. if (userDisplayName && typeof userDisplayName.value == "string" &&
  1026. userDisplayName.value) {
  1027. grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
  1028. }
  1029. let dbgGlobal = hooks.getGlobalDebugObject();
  1030. if (dbgGlobal) {
  1031. let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
  1032. if (script) {
  1033. grip.location = {
  1034. url: script.url,
  1035. line: script.startLine
  1036. };
  1037. }
  1038. }
  1039. return true;
  1040. }],
  1041. RegExp: [function ({obj, hooks}, grip) {
  1042. let str = RegExp.prototype.toString.call(obj.unsafeDereference());
  1043. grip.displayString = hooks.createValueGrip(str);
  1044. return true;
  1045. }],
  1046. Date: [function ({obj, hooks}, grip) {
  1047. let time = Date.prototype.getTime.call(obj.unsafeDereference());
  1048. grip.preview = {
  1049. timestamp: hooks.createValueGrip(time),
  1050. };
  1051. return true;
  1052. }],
  1053. Array: [function ({obj, hooks}, grip) {
  1054. let length = DevToolsUtils.getProperty(obj, "length");
  1055. if (typeof length != "number") {
  1056. return false;
  1057. }
  1058. grip.preview = {
  1059. kind: "ArrayLike",
  1060. length: length,
  1061. };
  1062. if (hooks.getGripDepth() > 1) {
  1063. return true;
  1064. }
  1065. let raw = obj.unsafeDereference();
  1066. let items = grip.preview.items = [];
  1067. for (let i = 0; i < length; ++i) {
  1068. // Array Xrays filter out various possibly-unsafe properties (like
  1069. // functions, and claim that the value is undefined instead. This
  1070. // is generally the right thing for privileged code accessing untrusted
  1071. // objects, but quite confusing for Object previews. So we manually
  1072. // override this protection by waiving Xrays on the array, and re-applying
  1073. // Xrays on any indexed value props that we pull off of it.
  1074. let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
  1075. if (desc && !desc.get && !desc.set) {
  1076. let value = Cu.unwaiveXrays(desc.value);
  1077. value = makeDebuggeeValueIfNeeded(obj, value);
  1078. items.push(hooks.createValueGrip(value));
  1079. } else {
  1080. items.push(null);
  1081. }
  1082. if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1083. break;
  1084. }
  1085. }
  1086. return true;
  1087. }],
  1088. Set: [function (objectActor, grip) {
  1089. let size = DevToolsUtils.getProperty(objectActor.obj, "size");
  1090. if (typeof size != "number") {
  1091. return false;
  1092. }
  1093. grip.preview = {
  1094. kind: "ArrayLike",
  1095. length: size,
  1096. };
  1097. // Avoid recursive object grips.
  1098. if (objectActor.hooks.getGripDepth() > 1) {
  1099. return true;
  1100. }
  1101. let items = grip.preview.items = [];
  1102. for (let item of enumSetEntries(objectActor)) {
  1103. items.push(item);
  1104. if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1105. break;
  1106. }
  1107. }
  1108. return true;
  1109. }],
  1110. WeakSet: [function (objectActor, grip) {
  1111. let enumEntries = enumWeakSetEntries(objectActor);
  1112. grip.preview = {
  1113. kind: "ArrayLike",
  1114. length: enumEntries.size
  1115. };
  1116. // Avoid recursive object grips.
  1117. if (objectActor.hooks.getGripDepth() > 1) {
  1118. return true;
  1119. }
  1120. let items = grip.preview.items = [];
  1121. for (let item of enumEntries) {
  1122. items.push(item);
  1123. if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1124. break;
  1125. }
  1126. }
  1127. return true;
  1128. }],
  1129. Map: [function (objectActor, grip) {
  1130. let size = DevToolsUtils.getProperty(objectActor.obj, "size");
  1131. if (typeof size != "number") {
  1132. return false;
  1133. }
  1134. grip.preview = {
  1135. kind: "MapLike",
  1136. size: size,
  1137. };
  1138. if (objectActor.hooks.getGripDepth() > 1) {
  1139. return true;
  1140. }
  1141. let entries = grip.preview.entries = [];
  1142. for (let entry of enumMapEntries(objectActor)) {
  1143. entries.push(entry);
  1144. if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1145. break;
  1146. }
  1147. }
  1148. return true;
  1149. }],
  1150. WeakMap: [function (objectActor, grip) {
  1151. let enumEntries = enumWeakMapEntries(objectActor);
  1152. grip.preview = {
  1153. kind: "MapLike",
  1154. size: enumEntries.size
  1155. };
  1156. if (objectActor.hooks.getGripDepth() > 1) {
  1157. return true;
  1158. }
  1159. let entries = grip.preview.entries = [];
  1160. for (let entry of enumEntries) {
  1161. entries.push(entry);
  1162. if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1163. break;
  1164. }
  1165. }
  1166. return true;
  1167. }],
  1168. DOMStringMap: [function ({obj, hooks}, grip, rawObj) {
  1169. if (!rawObj) {
  1170. return false;
  1171. }
  1172. let keys = obj.getOwnPropertyNames();
  1173. grip.preview = {
  1174. kind: "MapLike",
  1175. size: keys.length,
  1176. };
  1177. if (hooks.getGripDepth() > 1) {
  1178. return true;
  1179. }
  1180. let entries = grip.preview.entries = [];
  1181. for (let key of keys) {
  1182. let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
  1183. entries.push([key, hooks.createValueGrip(value)]);
  1184. if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1185. break;
  1186. }
  1187. }
  1188. return true;
  1189. }],
  1190. Proxy: [function ({obj, hooks}, grip, rawObj) {
  1191. grip.preview = {
  1192. kind: "Object",
  1193. ownProperties: Object.create(null),
  1194. ownPropertiesLength: 2
  1195. };
  1196. if (hooks.getGripDepth() > 1) {
  1197. return true;
  1198. }
  1199. grip.preview.ownProperties['<target>'] = {value: grip.proxyTarget};
  1200. grip.preview.ownProperties['<handler>'] = {value: grip.proxyHandler};
  1201. return true;
  1202. }],
  1203. };
  1204. /**
  1205. * Generic previewer for classes wrapping primitives, like String,
  1206. * Number and Boolean.
  1207. *
  1208. * @param string className
  1209. * Class name to expect.
  1210. * @param object classObj
  1211. * The class to expect, eg. String. The valueOf() method of the class is
  1212. * invoked on the given object.
  1213. * @param ObjectActor objectActor
  1214. * The object actor
  1215. * @param Object grip
  1216. * The result grip to fill in
  1217. * @return Booolean true if the object was handled, false otherwise
  1218. */
  1219. function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
  1220. let {obj, hooks} = objectActor;
  1221. if (!obj.proto || obj.proto.class != className) {
  1222. return false;
  1223. }
  1224. let v = null;
  1225. try {
  1226. v = classObj.prototype.valueOf.call(rawObj);
  1227. } catch (ex) {
  1228. // valueOf() can throw if the raw JS object is "misbehaved".
  1229. return false;
  1230. }
  1231. if (v === null) {
  1232. return false;
  1233. }
  1234. let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
  1235. if (!canHandle) {
  1236. return false;
  1237. }
  1238. grip.preview.wrappedValue =
  1239. hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
  1240. return true;
  1241. }
  1242. function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
  1243. let {obj, hooks} = objectActor;
  1244. if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
  1245. return false;
  1246. }
  1247. let i = 0, names = [];
  1248. let preview = grip.preview = {
  1249. kind: "Object",
  1250. ownProperties: Object.create(null),
  1251. };
  1252. try {
  1253. names = obj.getOwnPropertyNames();
  1254. } catch (ex) {
  1255. // Calling getOwnPropertyNames() on some wrapped native prototypes is not
  1256. // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
  1257. }
  1258. preview.ownPropertiesLength = names.length;
  1259. let length;
  1260. if (specialStringBehavior) {
  1261. length = DevToolsUtils.getProperty(obj, "length");
  1262. if (typeof length != "number") {
  1263. specialStringBehavior = false;
  1264. }
  1265. }
  1266. for (let name of names) {
  1267. if (specialStringBehavior && /^[0-9]+$/.test(name)) {
  1268. let num = parseInt(name, 10);
  1269. if (num.toString() === name && num >= 0 && num < length) {
  1270. continue;
  1271. }
  1272. }
  1273. let desc = objectActor._propertyDescriptor(name, true);
  1274. if (!desc) {
  1275. continue;
  1276. }
  1277. preview.ownProperties[name] = desc;
  1278. if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
  1279. break;
  1280. }
  1281. }
  1282. if (i < OBJECT_PREVIEW_MAX_ITEMS) {
  1283. preview.safeGetterValues = objectActor._findSafeGetterValues(
  1284. Object.keys(preview.ownProperties),
  1285. OBJECT_PREVIEW_MAX_ITEMS - i);
  1286. }
  1287. return true;
  1288. }
  1289. // Preview functions that do not rely on the object class.
  1290. DebuggerServer.ObjectActorPreviewers.Object = [
  1291. function TypedArray({obj, hooks}, grip) {
  1292. if (TYPED_ARRAY_CLASSES.indexOf(obj.class) == -1) {
  1293. return false;
  1294. }
  1295. let length = DevToolsUtils.getProperty(obj, "length");
  1296. if (typeof length != "number") {
  1297. return false;
  1298. }
  1299. grip.preview = {
  1300. kind: "ArrayLike",
  1301. length: length,
  1302. };
  1303. if (hooks.getGripDepth() > 1) {
  1304. return true;
  1305. }
  1306. let raw = obj.unsafeDereference();
  1307. let global = Cu.getGlobalForObject(DebuggerServer);
  1308. let classProto = global[obj.class].prototype;
  1309. // The Xray machinery for TypedArrays denies indexed access on the grounds
  1310. // that it's slow, and advises callers to do a structured clone instead.
  1311. let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
  1312. OBJECT_PREVIEW_MAX_ITEMS), global);
  1313. let items = grip.preview.items = [];
  1314. for (let i = 0; i < safeView.length; i++) {
  1315. items.push(safeView[i]);
  1316. }
  1317. return true;
  1318. },
  1319. function Error({obj, hooks}, grip) {
  1320. switch (obj.class) {
  1321. case "Error":
  1322. case "EvalError":
  1323. case "RangeError":
  1324. case "ReferenceError":
  1325. case "SyntaxError":
  1326. case "TypeError":
  1327. case "URIError":
  1328. let name = DevToolsUtils.getProperty(obj, "name");
  1329. let msg = DevToolsUtils.getProperty(obj, "message");
  1330. let stack = DevToolsUtils.getProperty(obj, "stack");
  1331. let fileName = DevToolsUtils.getProperty(obj, "fileName");
  1332. let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
  1333. let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
  1334. grip.preview = {
  1335. kind: "Error",
  1336. name: hooks.createValueGrip(name),
  1337. message: hooks.createValueGrip(msg),
  1338. stack: hooks.createValueGrip(stack),
  1339. fileName: hooks.createValueGrip(fileName),
  1340. lineNumber: hooks.createValueGrip(lineNumber),
  1341. columnNumber: hooks.createValueGrip(columnNumber),
  1342. };
  1343. return true;
  1344. default:
  1345. return false;
  1346. }
  1347. },
  1348. function CSSMediaRule({obj, hooks}, grip, rawObj) {
  1349. if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMCSSMediaRule)) {
  1350. return false;
  1351. }
  1352. grip.preview = {
  1353. kind: "ObjectWithText",
  1354. text: hooks.createValueGrip(rawObj.conditionText),
  1355. };
  1356. return true;
  1357. },
  1358. function CSSStyleRule({obj, hooks}, grip, rawObj) {
  1359. if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMCSSStyleRule)) {
  1360. return false;
  1361. }
  1362. grip.preview = {
  1363. kind: "ObjectWithText",
  1364. text: hooks.createValueGrip(rawObj.selectorText),
  1365. };
  1366. return true;
  1367. },
  1368. function ObjectWithURL({obj, hooks}, grip, rawObj) {
  1369. if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMCSSImportRule ||
  1370. rawObj instanceof Ci.nsIDOMCSSStyleSheet ||
  1371. rawObj instanceof Ci.nsIDOMLocation ||
  1372. rawObj instanceof Ci.nsIDOMWindow)) {
  1373. return false;
  1374. }
  1375. let url;
  1376. if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
  1377. url = rawObj.location.href;
  1378. } else if (rawObj.href) {
  1379. url = rawObj.href;
  1380. } else {
  1381. return false;
  1382. }
  1383. grip.preview = {
  1384. kind: "ObjectWithURL",
  1385. url: hooks.createValueGrip(url),
  1386. };
  1387. return true;
  1388. },
  1389. function ArrayLike({obj, hooks}, grip, rawObj) {
  1390. if (isWorker || !rawObj ||
  1391. obj.class != "DOMStringList" &&
  1392. obj.class != "DOMTokenList" &&
  1393. !(rawObj instanceof Ci.nsIDOMMozNamedAttrMap ||
  1394. rawObj instanceof Ci.nsIDOMCSSRuleList ||
  1395. rawObj instanceof Ci.nsIDOMCSSValueList ||
  1396. rawObj instanceof Ci.nsIDOMFileList ||
  1397. rawObj instanceof Ci.nsIDOMFontFaceList ||
  1398. rawObj instanceof Ci.nsIDOMMediaList ||
  1399. rawObj instanceof Ci.nsIDOMNodeList ||
  1400. rawObj instanceof Ci.nsIDOMStyleSheetList)) {
  1401. return false;
  1402. }
  1403. if (typeof rawObj.length != "number") {
  1404. return false;
  1405. }
  1406. grip.preview = {
  1407. kind: "ArrayLike",
  1408. length: rawObj.length,
  1409. };
  1410. if (hooks.getGripDepth() > 1) {
  1411. return true;
  1412. }
  1413. let items = grip.preview.items = [];
  1414. for (let i = 0; i < rawObj.length &&
  1415. items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
  1416. let value = makeDebuggeeValueIfNeeded(obj, rawObj[i]);
  1417. items.push(hooks.createValueGrip(value));
  1418. }
  1419. return true;
  1420. },
  1421. function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
  1422. if (isWorker || !rawObj ||
  1423. !(rawObj instanceof Ci.nsIDOMCSSStyleDeclaration)) {
  1424. return false;
  1425. }
  1426. grip.preview = {
  1427. kind: "MapLike",
  1428. size: rawObj.length,
  1429. };
  1430. let entries = grip.preview.entries = [];
  1431. for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
  1432. i < rawObj.length; i++) {
  1433. let prop = rawObj[i];
  1434. let value = rawObj.getPropertyValue(prop);
  1435. entries.push([prop, hooks.createValueGrip(value)]);
  1436. }
  1437. return true;
  1438. },
  1439. function DOMNode({obj, hooks}, grip, rawObj) {
  1440. if (isWorker || obj.class == "Object" || !rawObj ||
  1441. !(rawObj instanceof Ci.nsIDOMNode)) {
  1442. return false;
  1443. }
  1444. let preview = grip.preview = {
  1445. kind: "DOMNode",
  1446. nodeType: rawObj.nodeType,
  1447. nodeName: rawObj.nodeName,
  1448. };
  1449. if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
  1450. preview.location = hooks.createValueGrip(rawObj.location.href);
  1451. } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
  1452. preview.childNodesLength = rawObj.childNodes.length;
  1453. if (hooks.getGripDepth() < 2) {
  1454. preview.childNodes = [];
  1455. for (let node of rawObj.childNodes) {
  1456. let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
  1457. preview.childNodes.push(actor);
  1458. if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
  1459. break;
  1460. }
  1461. }
  1462. }
  1463. } else if (rawObj instanceof Ci.nsIDOMElement) {
  1464. // Add preview for DOM element attributes.
  1465. if (rawObj instanceof Ci.nsIDOMHTMLElement) {
  1466. preview.nodeName = preview.nodeName.toLowerCase();
  1467. }
  1468. let i = 0;
  1469. preview.attributes = {};
  1470. preview.attributesLength = rawObj.attributes.length;
  1471. for (let attr of rawObj.attributes) {
  1472. preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
  1473. }
  1474. } else if (rawObj instanceof Ci.nsIDOMAttr) {
  1475. preview.value = hooks.createValueGrip(rawObj.value);
  1476. } else if (rawObj instanceof Ci.nsIDOMText ||
  1477. rawObj instanceof Ci.nsIDOMComment) {
  1478. preview.textContent = hooks.createValueGrip(rawObj.textContent);
  1479. }
  1480. return true;
  1481. },
  1482. function DOMEvent({obj, hooks}, grip, rawObj) {
  1483. if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
  1484. return false;
  1485. }
  1486. let preview = grip.preview = {
  1487. kind: "DOMEvent",
  1488. type: rawObj.type,
  1489. properties: Object.create(null),
  1490. };
  1491. if (hooks.getGripDepth() < 2) {
  1492. let target = obj.makeDebuggeeValue(rawObj.target);
  1493. preview.target = hooks.createValueGrip(target);
  1494. }
  1495. let props = [];
  1496. if (rawObj instanceof Ci.nsIDOMMouseEvent) {
  1497. props.push("buttons", "clientX", "clientY", "layerX", "layerY");
  1498. } else if (rawObj instanceof Ci.nsIDOMKeyEvent) {
  1499. let modifiers = [];
  1500. if (rawObj.altKey) {
  1501. modifiers.push("Alt");
  1502. }
  1503. if (rawObj.ctrlKey) {
  1504. modifiers.push("Control");
  1505. }
  1506. if (rawObj.metaKey) {
  1507. modifiers.push("Meta");
  1508. }
  1509. if (rawObj.shiftKey) {
  1510. modifiers.push("Shift");
  1511. }
  1512. preview.eventKind = "key";
  1513. preview.modifiers = modifiers;
  1514. props.push("key", "charCode", "keyCode");
  1515. } else if (rawObj instanceof Ci.nsIDOMTransitionEvent) {
  1516. props.push("propertyName", "pseudoElement");
  1517. } else if (rawObj instanceof Ci.nsIDOMAnimationEvent) {
  1518. props.push("animationName", "pseudoElement");
  1519. } else if (rawObj instanceof Ci.nsIDOMClipboardEvent) {
  1520. props.push("clipboardData");
  1521. }
  1522. // Add event-specific properties.
  1523. for (let prop of props) {
  1524. let value = rawObj[prop];
  1525. if (value && (typeof value == "object" || typeof value == "function")) {
  1526. // Skip properties pointing to objects.
  1527. if (hooks.getGripDepth() > 1) {
  1528. continue;
  1529. }
  1530. value = obj.makeDebuggeeValue(value);
  1531. }
  1532. preview.properties[prop] = hooks.createValueGrip(value);
  1533. }
  1534. // Add any properties we find on the event object.
  1535. if (!props.length) {
  1536. let i = 0;
  1537. for (let prop in rawObj) {
  1538. let value = rawObj[prop];
  1539. if (prop == "target" || prop == "type" || value === null ||
  1540. typeof value == "function") {
  1541. continue;
  1542. }
  1543. if (value && typeof value == "object") {
  1544. if (hooks.getGripDepth() > 1) {
  1545. continue;
  1546. }
  1547. value = obj.makeDebuggeeValue(value);
  1548. }
  1549. preview.properties[prop] = hooks.createValueGrip(value);
  1550. if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
  1551. break;
  1552. }
  1553. }
  1554. }
  1555. return true;
  1556. },
  1557. function DOMException({obj, hooks}, grip, rawObj) {
  1558. if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
  1559. return false;
  1560. }
  1561. grip.preview = {
  1562. kind: "DOMException",
  1563. name: hooks.createValueGrip(rawObj.name),
  1564. message: hooks.createValueGrip(rawObj.message),
  1565. code: hooks.createValueGrip(rawObj.code),
  1566. result: hooks.createValueGrip(rawObj.result),
  1567. filename: hooks.createValueGrip(rawObj.filename),
  1568. lineNumber: hooks.createValueGrip(rawObj.lineNumber),
  1569. columnNumber: hooks.createValueGrip(rawObj.columnNumber),
  1570. };
  1571. return true;
  1572. },
  1573. function PseudoArray({obj, hooks}, grip, rawObj) {
  1574. let length;
  1575. let keys = obj.getOwnPropertyNames();
  1576. if (keys.length == 0) {
  1577. return false;
  1578. }
  1579. // If no item is going to be displayed in preview, better display as sparse object.
  1580. // The first key should contain the smallest integer index (if any).
  1581. if(keys[0] >= OBJECT_PREVIEW_MAX_ITEMS) {
  1582. return false;
  1583. }
  1584. // Pseudo-arrays should only have array indices and, optionally, a "length" property.
  1585. // Since integer indices are sorted first, check if the last property is "length".
  1586. if(keys[keys.length-1] === "length") {
  1587. keys.pop();
  1588. length = DevToolsUtils.getProperty(obj, "length");
  1589. } else {
  1590. // Otherwise, let length be the (presumably) greatest array index plus 1.
  1591. length = +keys[keys.length-1] + 1;
  1592. }
  1593. // Check if length is a valid array length, i.e. is a Uint32 number.
  1594. if(typeof length !== "number" || length >>> 0 !== length) {
  1595. return false;
  1596. }
  1597. // Ensure all keys are increasing array indices smaller than length. The order is not
  1598. // guaranteed for exotic objects but, in most cases, big array indices and properties
  1599. // which are not integer indices should be at the end. Then, iterating backwards
  1600. // allows us to return earlier when the object is not completely a pseudo-array.
  1601. let prev = length;
  1602. for(let i = keys.length - 1; i >= 0; --i) {
  1603. let key = keys[i];
  1604. let numKey = key >>> 0; // ToUint32(key)
  1605. if (numKey + '' !== key || numKey >= prev) {
  1606. return false;
  1607. }
  1608. prev = numKey;
  1609. }
  1610. grip.preview = {
  1611. kind: "ArrayLike",
  1612. length: length,
  1613. };
  1614. // Avoid recursive object grips.
  1615. if (hooks.getGripDepth() > 1) {
  1616. return true;
  1617. }
  1618. let items = grip.preview.items = [];
  1619. let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);
  1620. for (let i = 0; i < numItems; ++i) {
  1621. let desc = obj.getOwnPropertyDescriptor(i);
  1622. if (desc && 'value' in desc) {
  1623. items.push(hooks.createValueGrip(desc.value));
  1624. } else {
  1625. items.push(null);
  1626. }
  1627. }
  1628. return true;
  1629. },
  1630. function Object(objectActor, grip, rawObj) {
  1631. return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
  1632. },
  1633. ];
  1634. /**
  1635. * Get thisDebugger.Object referent's `promiseState`.
  1636. *
  1637. * @returns Object
  1638. * An object of one of the following forms:
  1639. * - { state: "pending" }
  1640. * - { state: "fulfilled", value }
  1641. * - { state: "rejected", reason }
  1642. */
  1643. function getPromiseState(obj) {
  1644. if (obj.class != "Promise") {
  1645. throw new Error(
  1646. "Can't call `getPromiseState` on `Debugger.Object`s that don't " +
  1647. "refer to Promise objects.");
  1648. }
  1649. let state = { state: obj.promiseState };
  1650. if (state.state === "fulfilled") {
  1651. state.value = obj.promiseValue;
  1652. } else if (state.state === "rejected") {
  1653. state.reason = obj.promiseReason;
  1654. }
  1655. return state;
  1656. }
  1657. /**
  1658. * Determine if a given value is non-primitive.
  1659. *
  1660. * @param Any value
  1661. * The value to test.
  1662. * @return Boolean
  1663. * Whether the value is non-primitive.
  1664. */
  1665. function isObject(value) {
  1666. const type = typeof value;
  1667. return type == "object" ? value !== null : type == "function";
  1668. }
  1669. /**
  1670. * Create a function that can safely stringify Debugger.Objects of a given
  1671. * builtin type.
  1672. *
  1673. * @param Function ctor
  1674. * The builtin class constructor.
  1675. * @return Function
  1676. * The stringifier for the class.
  1677. */
  1678. function createBuiltinStringifier(ctor) {
  1679. return obj => ctor.prototype.toString.call(obj.unsafeDereference());
  1680. }
  1681. /**
  1682. * Stringify a Debugger.Object-wrapped Error instance.
  1683. *
  1684. * @param Debugger.Object obj
  1685. * The object to stringify.
  1686. * @return String
  1687. * The stringification of the object.
  1688. */
  1689. function errorStringify(obj) {
  1690. let name = DevToolsUtils.getProperty(obj, "name");
  1691. if (name === "" || name === undefined) {
  1692. name = obj.class;
  1693. } else if (isObject(name)) {
  1694. name = stringify(name);
  1695. }
  1696. let message = DevToolsUtils.getProperty(obj, "message");
  1697. if (isObject(message)) {
  1698. message = stringify(message);
  1699. }
  1700. if (message === "" || message === undefined) {
  1701. return name;
  1702. }
  1703. return name + ": " + message;
  1704. }
  1705. /**
  1706. * Stringify a Debugger.Object based on its class.
  1707. *
  1708. * @param Debugger.Object obj
  1709. * The object to stringify.
  1710. * @return String
  1711. * The stringification for the object.
  1712. */
  1713. function stringify(obj) {
  1714. if (obj.class == "DeadObject") {
  1715. const error = new Error("Dead object encountered.");
  1716. DevToolsUtils.reportException("stringify", error);
  1717. return "<dead object>";
  1718. }
  1719. const stringifier = stringifiers[obj.class] || stringifiers.Object;
  1720. try {
  1721. return stringifier(obj);
  1722. } catch (e) {
  1723. DevToolsUtils.reportException("stringify", e);
  1724. return "<failed to stringify object>";
  1725. }
  1726. }
  1727. // Used to prevent infinite recursion when an array is found inside itself.
  1728. var seen = null;
  1729. var stringifiers = {
  1730. Error: errorStringify,
  1731. EvalError: errorStringify,
  1732. RangeError: errorStringify,
  1733. ReferenceError: errorStringify,
  1734. SyntaxError: errorStringify,
  1735. TypeError: errorStringify,
  1736. URIError: errorStringify,
  1737. Boolean: createBuiltinStringifier(Boolean),
  1738. Function: createBuiltinStringifier(Function),
  1739. Number: createBuiltinStringifier(Number),
  1740. RegExp: createBuiltinStringifier(RegExp),
  1741. String: createBuiltinStringifier(String),
  1742. Object: obj => "[object " + obj.class + "]",
  1743. Array: obj => {
  1744. // If we're at the top level then we need to create the Set for tracking
  1745. // previously stringified arrays.
  1746. const topLevel = !seen;
  1747. if (topLevel) {
  1748. seen = new Set();
  1749. } else if (seen.has(obj)) {
  1750. return "";
  1751. }
  1752. seen.add(obj);
  1753. const len = DevToolsUtils.getProperty(obj, "length");
  1754. let string = "";
  1755. // The following check is only required because the debuggee could possibly
  1756. // be a Proxy and return any value. For normal objects, array.length is
  1757. // always a non-negative integer.
  1758. if (typeof len == "number" && len > 0) {
  1759. for (let i = 0; i < len; i++) {
  1760. const desc = obj.getOwnPropertyDescriptor(i);
  1761. if (desc) {
  1762. const { value } = desc;
  1763. if (value != null) {
  1764. string += isObject(value) ? stringify(value) : value;
  1765. }
  1766. }
  1767. if (i < len - 1) {
  1768. string += ",";
  1769. }
  1770. }
  1771. }
  1772. if (topLevel) {
  1773. seen = null;
  1774. }
  1775. return string;
  1776. },
  1777. DOMException: obj => {
  1778. const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
  1779. const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
  1780. const code = DevToolsUtils.getProperty(obj, "code");
  1781. const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";
  1782. return '[Exception... "' + message + '" ' +
  1783. 'code: "' + code + '" ' +
  1784. 'nsresult: "0x' + result + " (" + name + ')"]';
  1785. },
  1786. Promise: obj => {
  1787. const { state, value, reason } = getPromiseState(obj);
  1788. let statePreview = state;
  1789. if (state != "pending") {
  1790. const settledValue = state === "fulfilled" ? value : reason;
  1791. statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
  1792. ? stringify(settledValue)
  1793. : settledValue);
  1794. }
  1795. return "Promise (" + statePreview + ")";
  1796. },
  1797. };
  1798. /**
  1799. * Make a debuggee value for the given object, if needed. Primitive values
  1800. * are left the same.
  1801. *
  1802. * Use case: you have a raw JS object (after unsafe dereference) and you want to
  1803. * send it to the client. In that case you need to use an ObjectActor which
  1804. * requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
  1805. * method works only for JS objects and functions.
  1806. *
  1807. * @param Debugger.Object obj
  1808. * @param any value
  1809. * @return object
  1810. */
  1811. function makeDebuggeeValueIfNeeded(obj, value) {
  1812. if (value && (typeof value == "object" || typeof value == "function")) {
  1813. return obj.makeDebuggeeValue(value);
  1814. }
  1815. return value;
  1816. }
  1817. /**
  1818. * Creates an actor for the specied "very long" string. "Very long" is specified
  1819. * at the server's discretion.
  1820. *
  1821. * @param string String
  1822. * The string.
  1823. */
  1824. function LongStringActor(string) {
  1825. this.string = string;
  1826. this.stringLength = string.length;
  1827. }
  1828. LongStringActor.prototype = {
  1829. actorPrefix: "longString",
  1830. disconnect: function () {
  1831. // Because longStringActors is not a weak map, we won't automatically leave
  1832. // it so we need to manually leave on disconnect so that we don't leak
  1833. // memory.
  1834. this._releaseActor();
  1835. },
  1836. /**
  1837. * Returns a grip for this actor for returning in a protocol message.
  1838. */
  1839. grip: function () {
  1840. return {
  1841. "type": "longString",
  1842. "initial": this.string.substring(
  1843. 0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
  1844. "length": this.stringLength,
  1845. "actor": this.actorID
  1846. };
  1847. },
  1848. /**
  1849. * Handle a request to extract part of this actor's string.
  1850. *
  1851. * @param request object
  1852. * The protocol request object.
  1853. */
  1854. onSubstring: function (request) {
  1855. return {
  1856. "from": this.actorID,
  1857. "substring": this.string.substring(request.start, request.end)
  1858. };
  1859. },
  1860. /**
  1861. * Handle a request to release this LongStringActor instance.
  1862. */
  1863. onRelease: function () {
  1864. // TODO: also check if registeredPool === threadActor.threadLifetimePool
  1865. // when the web console moves aray from manually releasing pause-scoped
  1866. // actors.
  1867. this._releaseActor();
  1868. this.registeredPool.removeActor(this);
  1869. return {};
  1870. },
  1871. _releaseActor: function () {
  1872. if (this.registeredPool && this.registeredPool.longStringActors) {
  1873. delete this.registeredPool.longStringActors[this.string];
  1874. }
  1875. }
  1876. };
  1877. LongStringActor.prototype.requestTypes = {
  1878. "substring": LongStringActor.prototype.onSubstring,
  1879. "release": LongStringActor.prototype.onRelease
  1880. };
  1881. /**
  1882. * Create a grip for the given debuggee value. If the value is an
  1883. * object, will create an actor with the given lifetime.
  1884. */
  1885. function createValueGrip(value, pool, makeObjectGrip) {
  1886. switch (typeof value) {
  1887. case "boolean":
  1888. return value;
  1889. case "string":
  1890. if (stringIsLong(value)) {
  1891. return longStringGrip(value, pool);
  1892. }
  1893. return value;
  1894. case "number":
  1895. if (value === Infinity) {
  1896. return { type: "Infinity" };
  1897. } else if (value === -Infinity) {
  1898. return { type: "-Infinity" };
  1899. } else if (Number.isNaN(value)) {
  1900. return { type: "NaN" };
  1901. } else if (!value && 1 / value === -Infinity) {
  1902. return { type: "-0" };
  1903. }
  1904. return value;
  1905. case "undefined":
  1906. return { type: "undefined" };
  1907. case "object":
  1908. if (value === null) {
  1909. return { type: "null" };
  1910. }
  1911. else if (value.optimizedOut ||
  1912. value.uninitialized ||
  1913. value.missingArguments) {
  1914. // The slot is optimized out, an uninitialized binding, or
  1915. // arguments on a dead scope
  1916. return {
  1917. type: "null",
  1918. optimizedOut: value.optimizedOut,
  1919. uninitialized: value.uninitialized,
  1920. missingArguments: value.missingArguments
  1921. };
  1922. }
  1923. return makeObjectGrip(value, pool);
  1924. case "symbol":
  1925. let form = {
  1926. type: "symbol"
  1927. };
  1928. let name = getSymbolName(value);
  1929. if (name !== undefined) {
  1930. form.name = createValueGrip(name, pool, makeObjectGrip);
  1931. }
  1932. return form;
  1933. default:
  1934. assert(false, "Failed to provide a grip for: " + value);
  1935. return null;
  1936. }
  1937. }
  1938. const symbolProtoToString = Symbol.prototype.toString;
  1939. function getSymbolName(symbol) {
  1940. const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
  1941. return name || undefined;
  1942. }
  1943. /**
  1944. * Returns true if the string is long enough to use a LongStringActor instead
  1945. * of passing the value directly over the protocol.
  1946. *
  1947. * @param str String
  1948. * The string we are checking the length of.
  1949. */
  1950. function stringIsLong(str) {
  1951. return str.length >= DebuggerServer.LONG_STRING_LENGTH;
  1952. }
  1953. /**
  1954. * Create a grip for the given string.
  1955. *
  1956. * @param str String
  1957. * The string we are creating a grip for.
  1958. * @param pool ActorPool
  1959. * The actor pool where the new actor will be added.
  1960. */
  1961. function longStringGrip(str, pool) {
  1962. if (!pool.longStringActors) {
  1963. pool.longStringActors = {};
  1964. }
  1965. if (pool.longStringActors.hasOwnProperty(str)) {
  1966. return pool.longStringActors[str].grip();
  1967. }
  1968. let actor = new LongStringActor(str);
  1969. pool.addActor(actor);
  1970. pool.longStringActors[str] = actor;
  1971. return actor.grip();
  1972. }
  1973. exports.ObjectActor = ObjectActor;
  1974. exports.PropertyIteratorActor = PropertyIteratorActor;
  1975. exports.LongStringActor = LongStringActor;
  1976. exports.createValueGrip = createValueGrip;
  1977. exports.stringIsLong = stringIsLong;
  1978. exports.longStringGrip = longStringGrip;