main.js 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. /**
  6. * Toolkit glue for the remote debugging protocol, loaded into the
  7. * debugging global.
  8. */
  9. var { Ci, Cc, CC, Cu, Cr } = require("chrome");
  10. var Services = require("Services");
  11. var { ActorPool, OriginalLocation, RegisteredActorFactory,
  12. ObservedActorFactory } = require("devtools/server/actors/common");
  13. var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
  14. require("devtools/shared/transport/transport");
  15. var DevToolsUtils = require("devtools/shared/DevToolsUtils");
  16. var { dumpn, dumpv } = DevToolsUtils;
  17. var flags = require("devtools/shared/flags");
  18. var EventEmitter = require("devtools/shared/event-emitter");
  19. var Promise = require("promise");
  20. var SyncPromise = require("devtools/shared/deprecated-sync-thenables");
  21. DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
  22. let { DebuggerSocket } = require("devtools/shared/security/socket");
  23. return DebuggerSocket;
  24. });
  25. DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
  26. return require("devtools/shared/security/auth");
  27. });
  28. DevToolsUtils.defineLazyGetter(this, "generateUUID", () => {
  29. let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
  30. .getService(Ci.nsIUUIDGenerator);
  31. return generateUUID;
  32. });
  33. // On B2G, `this` != Global scope, so `Ci` won't be binded on `this`
  34. // (i.e. this.Ci is undefined) Then later, when using loadSubScript,
  35. // Ci,... won't be defined for sub scripts.
  36. this.Ci = Ci;
  37. this.Cc = Cc;
  38. this.CC = CC;
  39. this.Cu = Cu;
  40. this.Cr = Cr;
  41. this.Services = Services;
  42. this.ActorPool = ActorPool;
  43. this.DevToolsUtils = DevToolsUtils;
  44. this.dumpn = dumpn;
  45. this.dumpv = dumpv;
  46. // Overload `Components` to prevent SDK loader exception on Components
  47. // object usage
  48. Object.defineProperty(this, "Components", {
  49. get() {
  50. return require("chrome").components;
  51. }
  52. });
  53. if (isWorker) {
  54. flags.wantLogging = true;
  55. flags.wantVerbose = true;
  56. } else {
  57. const LOG_PREF = "devtools.debugger.log";
  58. const VERBOSE_PREF = "devtools.debugger.log.verbose";
  59. flags.wantLogging = Services.prefs.getBoolPref(LOG_PREF);
  60. flags.wantVerbose =
  61. Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
  62. Services.prefs.getBoolPref(VERBOSE_PREF);
  63. }
  64. function loadSubScript(url) {
  65. try {
  66. let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
  67. .getService(Ci.mozIJSSubScriptLoader);
  68. loader.loadSubScript(url, this);
  69. } catch (e) {
  70. let errorStr = "Error loading: " + url + ":\n" +
  71. (e.fileName ? "at " + e.fileName + " : " + e.lineNumber + "\n" : "") +
  72. e + " - " + e.stack + "\n";
  73. dump(errorStr);
  74. reportError(errorStr);
  75. throw e;
  76. }
  77. }
  78. loader.lazyRequireGetter(this, "events", "sdk/event/core");
  79. var gRegisteredModules = Object.create(null);
  80. /**
  81. * The ModuleAPI object is passed to modules loaded using the
  82. * DebuggerServer.registerModule() API. Modules can use this
  83. * object to register actor factories.
  84. * Factories registered through the module API will be removed
  85. * when the module is unregistered or when the server is
  86. * destroyed.
  87. */
  88. function ModuleAPI() {
  89. let activeTabActors = new Set();
  90. let activeGlobalActors = new Set();
  91. return {
  92. // See DebuggerServer.setRootActor for a description.
  93. setRootActor(factory) {
  94. DebuggerServer.setRootActor(factory);
  95. },
  96. // See DebuggerServer.addGlobalActor for a description.
  97. addGlobalActor(factory, name) {
  98. DebuggerServer.addGlobalActor(factory, name);
  99. activeGlobalActors.add(factory);
  100. },
  101. // See DebuggerServer.removeGlobalActor for a description.
  102. removeGlobalActor(factory) {
  103. DebuggerServer.removeGlobalActor(factory);
  104. activeGlobalActors.delete(factory);
  105. },
  106. // See DebuggerServer.addTabActor for a description.
  107. addTabActor(factory, name) {
  108. DebuggerServer.addTabActor(factory, name);
  109. activeTabActors.add(factory);
  110. },
  111. // See DebuggerServer.removeTabActor for a description.
  112. removeTabActor(factory) {
  113. DebuggerServer.removeTabActor(factory);
  114. activeTabActors.delete(factory);
  115. },
  116. // Destroy the module API object, unregistering any
  117. // factories registered by the module.
  118. destroy() {
  119. for (let factory of activeTabActors) {
  120. DebuggerServer.removeTabActor(factory);
  121. }
  122. activeTabActors = null;
  123. for (let factory of activeGlobalActors) {
  124. DebuggerServer.removeGlobalActor(factory);
  125. }
  126. activeGlobalActors = null;
  127. }
  128. };
  129. }
  130. /** *
  131. * Public API
  132. */
  133. var DebuggerServer = {
  134. _listeners: [],
  135. _initialized: false,
  136. // Map of global actor names to actor constructors provided by extensions.
  137. globalActorFactories: {},
  138. // Map of tab actor names to actor constructors provided by extensions.
  139. tabActorFactories: {},
  140. LONG_STRING_LENGTH: 10000,
  141. LONG_STRING_INITIAL_LENGTH: 1000,
  142. LONG_STRING_READ_LENGTH: 65 * 1024,
  143. /**
  144. * The windowtype of the chrome window to use for actors that use the global
  145. * window (i.e the global style editor). Set this to your main window type,
  146. * for example "navigator:browser".
  147. */
  148. chromeWindowType: null,
  149. /**
  150. * Allow debugging chrome of (parent or child) processes.
  151. */
  152. allowChromeProcess: false,
  153. /**
  154. * Initialize the debugger server.
  155. */
  156. init() {
  157. if (this.initialized) {
  158. return;
  159. }
  160. this._connections = {};
  161. this._nextConnID = 0;
  162. this._initialized = true;
  163. },
  164. get protocol() {
  165. return require("devtools/shared/protocol");
  166. },
  167. get initialized() {
  168. return this._initialized;
  169. },
  170. /**
  171. * Performs cleanup tasks before shutting down the debugger server. Such tasks
  172. * include clearing any actor constructors added at runtime. This method
  173. * should be called whenever a debugger server is no longer useful, to avoid
  174. * memory leaks. After this method returns, the debugger server must be
  175. * initialized again before use.
  176. */
  177. destroy() {
  178. if (!this._initialized) {
  179. return;
  180. }
  181. for (let connID of Object.getOwnPropertyNames(this._connections)) {
  182. this._connections[connID].close();
  183. }
  184. for (let id of Object.getOwnPropertyNames(gRegisteredModules)) {
  185. this.unregisterModule(id);
  186. }
  187. gRegisteredModules = Object.create(null);
  188. this.closeAllListeners();
  189. this.globalActorFactories = {};
  190. this.tabActorFactories = {};
  191. this._initialized = false;
  192. dumpn("Debugger server is shut down.");
  193. },
  194. /**
  195. * Raises an exception if the server has not been properly initialized.
  196. */
  197. _checkInit() {
  198. if (!this._initialized) {
  199. throw new Error("DebuggerServer has not been initialized.");
  200. }
  201. if (!this.createRootActor) {
  202. throw new Error("Use DebuggerServer.addActors() to add a root actor " +
  203. "implementation.");
  204. }
  205. },
  206. /**
  207. * Load a subscript into the debugging global.
  208. *
  209. * @param url string A url that will be loaded as a subscript into the
  210. * debugging global. The user must load at least one script
  211. * that implements a createRootActor() function to create the
  212. * server's root actor.
  213. */
  214. addActors(url) {
  215. loadSubScript.call(this, url);
  216. },
  217. /**
  218. * Register a CommonJS module with the debugger server.
  219. * @param id string
  220. * The ID of a CommonJS module. This module must export 'register'
  221. * and 'unregister' functions if no `options` argument is given.
  222. * If `options` is set, the actor is going to be registered
  223. * immediately, but loaded only when a client starts sending packets
  224. * to an actor with the same id.
  225. *
  226. * @param options object (optional)
  227. * This parameter is still optional, but not providing it is
  228. * deprecated and will result in eagerly loading the actor module
  229. * with the memory overhead that entails.
  230. * An object with 3 mandatory attributes:
  231. * - prefix (string):
  232. * The prefix of an actor is used to compute:
  233. * - the `actorID` of each new actor instance (ex: prefix1).
  234. * (See ActorPool.addActor)
  235. * - the actor name in the listTabs request. Sending a listTabs
  236. * request to the root actor returns actor IDs. IDs are in
  237. * dictionaries, with actor names as keys and actor IDs as values.
  238. * The actor name is the prefix to which the "Actor" string is
  239. * appended. So for an actor with the `console` prefix, the actor
  240. * name will be `consoleActor`.
  241. * - constructor (string):
  242. * the name of the exported symbol to be used as the actor
  243. * constructor.
  244. * - type (a dictionary of booleans with following attribute names):
  245. * - "global"
  246. * registers a global actor instance, if true.
  247. * A global actor has the root actor as its parent.
  248. * - "tab"
  249. * registers a tab actor instance, if true.
  250. * A new actor will be created for each tab and each app.
  251. */
  252. registerModule(id, options) {
  253. if (id in gRegisteredModules) {
  254. throw new Error("Tried to register a module twice: " + id + "\n");
  255. }
  256. if (options) {
  257. // Lazy loaded actors
  258. let {prefix, constructor, type} = options;
  259. if (typeof (prefix) !== "string") {
  260. throw new Error(`Lazy actor definition for '${id}' requires a string ` +
  261. `'prefix' option.`);
  262. }
  263. if (typeof (constructor) !== "string") {
  264. throw new Error(`Lazy actor definition for '${id}' requires a string ` +
  265. `'constructor' option.`);
  266. }
  267. if (!("global" in type) && !("tab" in type)) {
  268. throw new Error(`Lazy actor definition for '${id}' requires a dictionary ` +
  269. `'type' option whose attributes can be 'global' or 'tab'.`);
  270. }
  271. let name = prefix + "Actor";
  272. let mod = {
  273. id: id,
  274. prefix: prefix,
  275. constructorName: constructor,
  276. type: type,
  277. globalActor: type.global,
  278. tabActor: type.tab
  279. };
  280. gRegisteredModules[id] = mod;
  281. if (mod.tabActor) {
  282. this.addTabActor(mod, name);
  283. }
  284. if (mod.globalActor) {
  285. this.addGlobalActor(mod, name);
  286. }
  287. } else {
  288. // Deprecated actors being loaded at startup
  289. let moduleAPI = ModuleAPI();
  290. let mod = require(id);
  291. mod.register(moduleAPI);
  292. gRegisteredModules[id] = {
  293. module: mod,
  294. api: moduleAPI
  295. };
  296. }
  297. },
  298. /**
  299. * Returns true if a module id has been registered.
  300. */
  301. isModuleRegistered(id) {
  302. return (id in gRegisteredModules);
  303. },
  304. /**
  305. * Unregister a previously-loaded CommonJS module from the debugger server.
  306. */
  307. unregisterModule(id) {
  308. let mod = gRegisteredModules[id];
  309. if (!mod) {
  310. throw new Error("Tried to unregister a module that was not previously registered.");
  311. }
  312. // Lazy actors
  313. if (mod.tabActor) {
  314. this.removeTabActor(mod);
  315. }
  316. if (mod.globalActor) {
  317. this.removeGlobalActor(mod);
  318. }
  319. if (mod.module) {
  320. // Deprecated non-lazy module API
  321. mod.module.unregister(mod.api);
  322. mod.api.destroy();
  323. }
  324. delete gRegisteredModules[id];
  325. },
  326. /**
  327. * Install Firefox-specific actors.
  328. *
  329. * /!\ Be careful when adding a new actor, especially global actors.
  330. * Any new global actor will be exposed and returned by the root actor.
  331. *
  332. * That's the reason why tab actors aren't loaded on demand via
  333. * restrictPrivileges=true, to prevent exposing them on b2g parent process's
  334. * root actor.
  335. */
  336. addBrowserActors(windowType = "navigator:browser", restrictPrivileges = false) {
  337. this.chromeWindowType = windowType;
  338. this.registerModule("devtools/server/actors/webbrowser");
  339. if (!restrictPrivileges) {
  340. this.addTabActors();
  341. this.registerModule("devtools/server/actors/preference", {
  342. prefix: "preference",
  343. constructor: "PreferenceActor",
  344. type: { global: true }
  345. });
  346. this.registerModule("devtools/server/actors/actor-registry", {
  347. prefix: "actorRegistry",
  348. constructor: "ActorRegistryActor",
  349. type: { global: true }
  350. });
  351. }
  352. if (Services.prefs.getBoolPref("dom.mozSettings.enabled")) {
  353. this.registerModule("devtools/server/actors/settings", {
  354. prefix: "settings",
  355. constructor: "SettingsActor",
  356. type: { global: true }
  357. });
  358. }
  359. this.registerModule("devtools/server/actors/addons", {
  360. prefix: "addons",
  361. constructor: "AddonsActor",
  362. type: { global: true }
  363. });
  364. this.registerModule("devtools/server/actors/device", {
  365. prefix: "device",
  366. constructor: "DeviceActor",
  367. type: { global: true }
  368. });
  369. this.registerModule("devtools/server/actors/director-registry", {
  370. prefix: "directorRegistry",
  371. constructor: "DirectorRegistryActor",
  372. type: { global: true }
  373. });
  374. this.registerModule("devtools/server/actors/heap-snapshot-file", {
  375. prefix: "heapSnapshotFile",
  376. constructor: "HeapSnapshotFileActor",
  377. type: { global: true }
  378. });
  379. },
  380. /**
  381. * Install tab actors in documents loaded in content childs
  382. */
  383. addChildActors() {
  384. // In case of apps being loaded in parent process, DebuggerServer is already
  385. // initialized and browser actors are already loaded,
  386. // but childtab.js hasn't been loaded yet.
  387. if (!DebuggerServer.tabActorFactories.hasOwnProperty("consoleActor")) {
  388. this.addTabActors();
  389. }
  390. // But webbrowser.js and childtab.js aren't loaded from shell.js.
  391. if (!this.isModuleRegistered("devtools/server/actors/webbrowser")) {
  392. this.registerModule("devtools/server/actors/webbrowser");
  393. }
  394. if (!("ContentActor" in this)) {
  395. this.addActors("resource://devtools/server/actors/childtab.js");
  396. }
  397. },
  398. /**
  399. * Install tab actors.
  400. */
  401. addTabActors() {
  402. this.registerModule("devtools/server/actors/webconsole", {
  403. prefix: "console",
  404. constructor: "WebConsoleActor",
  405. type: { tab: true }
  406. });
  407. this.registerModule("devtools/server/actors/inspector", {
  408. prefix: "inspector",
  409. constructor: "InspectorActor",
  410. type: { tab: true }
  411. });
  412. this.registerModule("devtools/server/actors/call-watcher", {
  413. prefix: "callWatcher",
  414. constructor: "CallWatcherActor",
  415. type: { tab: true }
  416. });
  417. this.registerModule("devtools/server/actors/canvas", {
  418. prefix: "canvas",
  419. constructor: "CanvasActor",
  420. type: { tab: true }
  421. });
  422. this.registerModule("devtools/server/actors/webgl", {
  423. prefix: "webgl",
  424. constructor: "WebGLActor",
  425. type: { tab: true }
  426. });
  427. this.registerModule("devtools/server/actors/webaudio", {
  428. prefix: "webaudio",
  429. constructor: "WebAudioActor",
  430. type: { tab: true }
  431. });
  432. this.registerModule("devtools/server/actors/stylesheets", {
  433. prefix: "styleSheets",
  434. constructor: "StyleSheetsActor",
  435. type: { tab: true }
  436. });
  437. this.registerModule("devtools/server/actors/styleeditor", {
  438. prefix: "styleEditor",
  439. constructor: "StyleEditorActor",
  440. type: { tab: true }
  441. });
  442. this.registerModule("devtools/server/actors/storage", {
  443. prefix: "storage",
  444. constructor: "StorageActor",
  445. type: { tab: true }
  446. });
  447. this.registerModule("devtools/server/actors/gcli", {
  448. prefix: "gcli",
  449. constructor: "GcliActor",
  450. type: { tab: true }
  451. });
  452. this.registerModule("devtools/server/actors/memory", {
  453. prefix: "memory",
  454. constructor: "MemoryActor",
  455. type: { tab: true }
  456. });
  457. this.registerModule("devtools/server/actors/framerate", {
  458. prefix: "framerate",
  459. constructor: "FramerateActor",
  460. type: { tab: true }
  461. });
  462. this.registerModule("devtools/server/actors/eventlooplag", {
  463. prefix: "eventLoopLag",
  464. constructor: "EventLoopLagActor",
  465. type: { tab: true }
  466. });
  467. this.registerModule("devtools/server/actors/reflow", {
  468. prefix: "reflow",
  469. constructor: "ReflowActor",
  470. type: { tab: true }
  471. });
  472. this.registerModule("devtools/server/actors/css-properties", {
  473. prefix: "cssProperties",
  474. constructor: "CssPropertiesActor",
  475. type: { tab: true }
  476. });
  477. this.registerModule("devtools/server/actors/csscoverage", {
  478. prefix: "cssUsage",
  479. constructor: "CSSUsageActor",
  480. type: { tab: true }
  481. });
  482. this.registerModule("devtools/server/actors/monitor", {
  483. prefix: "monitor",
  484. constructor: "MonitorActor",
  485. type: { tab: true }
  486. });
  487. this.registerModule("devtools/server/actors/timeline", {
  488. prefix: "timeline",
  489. constructor: "TimelineActor",
  490. type: { tab: true }
  491. });
  492. this.registerModule("devtools/server/actors/director-manager", {
  493. prefix: "directorManager",
  494. constructor: "DirectorManagerActor",
  495. type: { global: false, tab: true }
  496. });
  497. if ("nsIProfiler" in Ci) {
  498. this.registerModule("devtools/server/actors/profiler", {
  499. prefix: "profiler",
  500. constructor: "ProfilerActor",
  501. type: { tab: true }
  502. });
  503. this.registerModule("devtools/server/actors/performance", {
  504. prefix: "performance",
  505. constructor: "PerformanceActor",
  506. type: { tab: true }
  507. });
  508. }
  509. this.registerModule("devtools/server/actors/animation", {
  510. prefix: "animations",
  511. constructor: "AnimationsActor",
  512. type: { tab: true }
  513. });
  514. this.registerModule("devtools/server/actors/promises", {
  515. prefix: "promises",
  516. constructor: "PromisesActor",
  517. type: { tab: true }
  518. });
  519. this.registerModule("devtools/server/actors/performance-entries", {
  520. prefix: "performanceEntries",
  521. constructor: "PerformanceEntriesActor",
  522. type: { tab: true }
  523. });
  524. this.registerModule("devtools/server/actors/emulation", {
  525. prefix: "emulation",
  526. constructor: "EmulationActor",
  527. type: { tab: true }
  528. });
  529. },
  530. /**
  531. * Passes a set of options to the BrowserAddonActors for the given ID.
  532. *
  533. * @param id string
  534. * The ID of the add-on to pass the options to
  535. * @param options object
  536. * The options.
  537. * @return a promise that will be resolved when complete.
  538. */
  539. setAddonOptions(id, options) {
  540. if (!this._initialized) {
  541. return Promise.resolve();
  542. }
  543. let promises = [];
  544. // Pass to all connections
  545. for (let connID of Object.getOwnPropertyNames(this._connections)) {
  546. promises.push(this._connections[connID].setAddonOptions(id, options));
  547. }
  548. return SyncPromise.all(promises);
  549. },
  550. get listeningSockets() {
  551. return this._listeners.length;
  552. },
  553. /**
  554. * Creates a socket listener for remote debugger connections.
  555. *
  556. * After calling this, set some socket options, such as the port / path to
  557. * listen on, and then call |open| on the listener.
  558. *
  559. * See SocketListener in devtools/shared/security/socket.js for available
  560. * options.
  561. *
  562. * @return SocketListener
  563. * A SocketListener instance that is waiting to be configured and
  564. * opened is returned. This single listener can be closed at any
  565. * later time by calling |close| on the SocketListener. If remote
  566. * connections are disabled, an error is thrown.
  567. */
  568. createListener() {
  569. if (!Services.prefs.getBoolPref("devtools.debugger.remote-enabled")) {
  570. throw new Error("Can't create listener, remote debugging disabled");
  571. }
  572. this._checkInit();
  573. return DebuggerSocket.createListener();
  574. },
  575. /**
  576. * Add a SocketListener instance to the server's set of active
  577. * SocketListeners. This is called by a SocketListener after it is opened.
  578. */
  579. _addListener(listener) {
  580. this._listeners.push(listener);
  581. },
  582. /**
  583. * Remove a SocketListener instance from the server's set of active
  584. * SocketListeners. This is called by a SocketListener after it is closed.
  585. */
  586. _removeListener(listener) {
  587. this._listeners = this._listeners.filter(l => l !== listener);
  588. },
  589. /**
  590. * Closes and forgets all previously opened listeners.
  591. *
  592. * @return boolean
  593. * Whether any listeners were actually closed.
  594. */
  595. closeAllListeners() {
  596. if (!this.listeningSockets) {
  597. return false;
  598. }
  599. for (let listener of this._listeners) {
  600. listener.close();
  601. }
  602. return true;
  603. },
  604. /**
  605. * Creates a new connection to the local debugger speaking over a fake
  606. * transport. This connection results in straightforward calls to the onPacket
  607. * handlers of each side.
  608. *
  609. * @param prefix string [optional]
  610. * If given, all actors in this connection will have names starting
  611. * with |prefix + '/'|.
  612. * @returns a client-side DebuggerTransport for communicating with
  613. * the newly-created connection.
  614. */
  615. connectPipe(prefix) {
  616. this._checkInit();
  617. let serverTransport = new LocalDebuggerTransport();
  618. let clientTransport = new LocalDebuggerTransport(serverTransport);
  619. serverTransport.other = clientTransport;
  620. let connection = this._onConnection(serverTransport, prefix);
  621. // I'm putting this here because I trust you.
  622. //
  623. // There are times, when using a local connection, when you're going
  624. // to be tempted to just get direct access to the server. Resist that
  625. // temptation! If you succumb to that temptation, you will make the
  626. // fine developers that work on Fennec and Firefox OS sad. They're
  627. // professionals, they'll try to act like they understand, but deep
  628. // down you'll know that you hurt them.
  629. //
  630. // This reference allows you to give in to that temptation. There are
  631. // times this makes sense: tests, for example, and while porting a
  632. // previously local-only codebase to the remote protocol.
  633. //
  634. // But every time you use this, you will feel the shame of having
  635. // used a property that starts with a '_'.
  636. clientTransport._serverConnection = connection;
  637. return clientTransport;
  638. },
  639. /**
  640. * In a content child process, create a new connection that exchanges
  641. * nsIMessageSender messages with our parent process.
  642. *
  643. * @param prefix
  644. * The prefix we should use in our nsIMessageSender message names and
  645. * actor names. This connection will use messages named
  646. * "debug:<prefix>:packet", and all its actors will have names
  647. * beginning with "<prefix>/".
  648. */
  649. connectToParent(prefix, scopeOrManager) {
  650. this._checkInit();
  651. let transport = isWorker ?
  652. new WorkerDebuggerTransport(scopeOrManager, prefix) :
  653. new ChildDebuggerTransport(scopeOrManager, prefix);
  654. return this._onConnection(transport, prefix, true);
  655. },
  656. connectToContent(connection, mm) {
  657. let deferred = SyncPromise.defer();
  658. let prefix = connection.allocID("content-process");
  659. let actor, childTransport;
  660. mm.addMessageListener("debug:content-process-actor", function listener(msg) {
  661. // Arbitrarily choose the first content process to reply
  662. // XXX: This code needs to be updated if we use more than one content process
  663. mm.removeMessageListener("debug:content-process-actor", listener);
  664. // Pipe Debugger message from/to parent/child via the message manager
  665. childTransport = new ChildDebuggerTransport(mm, prefix);
  666. childTransport.hooks = {
  667. onPacket: connection.send.bind(connection),
  668. onClosed() {}
  669. };
  670. childTransport.ready();
  671. connection.setForwarding(prefix, childTransport);
  672. dumpn("establishing forwarding for process with prefix " + prefix);
  673. actor = msg.json.actor;
  674. deferred.resolve(actor);
  675. });
  676. mm.sendAsyncMessage("DevTools:InitDebuggerServer", {
  677. prefix: prefix
  678. });
  679. function onClose() {
  680. Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
  681. events.off(connection, "closed", onClose);
  682. if (childTransport) {
  683. // If we have a child transport, the actor has already
  684. // been created. We need to stop using this message manager.
  685. childTransport.close();
  686. childTransport = null;
  687. connection.cancelForwarding(prefix);
  688. // ... and notify the child process to clean the tab actors.
  689. try {
  690. mm.sendAsyncMessage("debug:content-process-destroy");
  691. } catch (e) {
  692. // Nothing to do
  693. }
  694. }
  695. }
  696. let onMessageManagerClose = DevToolsUtils.makeInfallible((subject, topic, data) => {
  697. if (subject == mm) {
  698. onClose();
  699. connection.send({ from: actor.actor, type: "tabDetached" });
  700. }
  701. });
  702. Services.obs.addObserver(onMessageManagerClose,
  703. "message-manager-close", false);
  704. events.on(connection, "closed", onClose);
  705. return deferred.promise;
  706. },
  707. connectToWorker(connection, dbg, id, options) {
  708. return new Promise((resolve, reject) => {
  709. // Step 1: Ensure the worker debugger is initialized.
  710. if (!dbg.isInitialized) {
  711. dbg.initialize("resource://devtools/server/worker.js");
  712. // Create a listener for rpc requests from the worker debugger. Only do
  713. // this once, when the worker debugger is first initialized, rather than
  714. // for each connection.
  715. let listener = {
  716. onClose: () => {
  717. dbg.removeListener(listener);
  718. },
  719. onMessage: (message) => {
  720. message = JSON.parse(message);
  721. if (message.type !== "rpc") {
  722. return;
  723. }
  724. Promise.resolve().then(() => {
  725. let method = {
  726. "fetch": DevToolsUtils.fetch,
  727. }[message.method];
  728. if (!method) {
  729. throw Error("Unknown method: " + message.method);
  730. }
  731. return method.apply(undefined, message.params);
  732. }).then((value) => {
  733. dbg.postMessage(JSON.stringify({
  734. type: "rpc",
  735. result: value,
  736. error: null,
  737. id: message.id
  738. }));
  739. }, (reason) => {
  740. dbg.postMessage(JSON.stringify({
  741. type: "rpc",
  742. result: null,
  743. error: reason,
  744. id: message.id
  745. }));
  746. });
  747. }
  748. };
  749. dbg.addListener(listener);
  750. }
  751. // Step 2: Send a connect request to the worker debugger.
  752. dbg.postMessage(JSON.stringify({
  753. type: "connect",
  754. id,
  755. options,
  756. }));
  757. // Steps 3-5 are performed on the worker thread (see worker.js).
  758. // Step 6: Wait for a connection response from the worker debugger.
  759. let listener = {
  760. onClose: () => {
  761. dbg.removeListener(listener);
  762. reject("closed");
  763. },
  764. onMessage: (message) => {
  765. message = JSON.parse(message);
  766. if (message.type !== "connected" || message.id !== id) {
  767. return;
  768. }
  769. // The initial connection message has been received, don't
  770. // need to listen any longer
  771. dbg.removeListener(listener);
  772. // Step 7: Create a transport for the connection to the worker.
  773. let transport = new WorkerDebuggerTransport(dbg, id);
  774. transport.ready();
  775. transport.hooks = {
  776. onClosed: () => {
  777. if (!dbg.isClosed) {
  778. // If the worker happens to be shutting down while we are trying
  779. // to close the connection, there is a small interval during
  780. // which no more runnables can be dispatched to the worker, but
  781. // the worker debugger has not yet been closed. In that case,
  782. // the call to postMessage below will fail. The onClosed hook on
  783. // DebuggerTransport is not supposed to throw exceptions, so we
  784. // need to make sure to catch these early.
  785. try {
  786. dbg.postMessage(JSON.stringify({
  787. type: "disconnect",
  788. id,
  789. }));
  790. } catch (e) {
  791. // We can safely ignore these exceptions. The only time the
  792. // call to postMessage can fail is if the worker is either
  793. // shutting down, or has finished shutting down. In both
  794. // cases, there is nothing to clean up, so we don't care
  795. // whether this message arrives or not.
  796. }
  797. }
  798. connection.cancelForwarding(id);
  799. },
  800. onPacket: (packet) => {
  801. // Ensure that any packets received from the server on the worker
  802. // thread are forwarded to the client on the main thread, as if
  803. // they had been sent by the server on the main thread.
  804. connection.send(packet);
  805. }
  806. };
  807. // Ensure that any packets received from the client on the main thread
  808. // to actors on the worker thread are forwarded to the server on the
  809. // worker thread.
  810. connection.setForwarding(id, transport);
  811. resolve({
  812. threadActor: message.threadActor,
  813. consoleActor: message.consoleActor,
  814. transport: transport
  815. });
  816. }
  817. };
  818. dbg.addListener(listener);
  819. });
  820. },
  821. /**
  822. * Check if the server is running in the child process.
  823. */
  824. get isInChildProcess() {
  825. return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
  826. },
  827. /**
  828. * In a chrome parent process, ask all content child processes
  829. * to execute a given module setup helper.
  830. *
  831. * @param module
  832. * The module to be required
  833. * @param setupChild
  834. * The name of the setup helper exported by the above module
  835. * (setup helper signature: function ({mm}) { ... })
  836. * @param waitForEval (optional)
  837. * If true, the returned promise only resolves once code in child
  838. * is evaluated
  839. */
  840. setupInChild({ module, setupChild, args, waitForEval }) {
  841. if (this._childMessageManagers.size == 0) {
  842. return Promise.resolve();
  843. }
  844. let deferred = Promise.defer();
  845. // If waitForEval is set, pass a unique id and expect child.js to send
  846. // a message back once the code in child is evaluated.
  847. if (typeof (waitForEval) != "boolean") {
  848. waitForEval = false;
  849. }
  850. let count = this._childMessageManagers.size;
  851. let id = waitForEval ? generateUUID().toString() : null;
  852. this._childMessageManagers.forEach(mm => {
  853. if (waitForEval) {
  854. // Listen for the end of each child execution
  855. let evalListener = msg => {
  856. if (msg.data.id !== id) {
  857. return;
  858. }
  859. mm.removeMessageListener("debug:setup-in-child-response", evalListener);
  860. if (--count === 0) {
  861. deferred.resolve();
  862. }
  863. };
  864. mm.addMessageListener("debug:setup-in-child-response", evalListener);
  865. }
  866. mm.sendAsyncMessage("debug:setup-in-child", {
  867. module: module,
  868. setupChild: setupChild,
  869. args: args,
  870. id: id,
  871. });
  872. });
  873. if (waitForEval) {
  874. return deferred.promise;
  875. }
  876. return Promise.resolve();
  877. },
  878. /**
  879. * Live list of all currenctly attached child's message managers.
  880. */
  881. _childMessageManagers: new Set(),
  882. /**
  883. * Connect to a child process.
  884. *
  885. * @param object connection
  886. * The debugger server connection to use.
  887. * @param nsIDOMElement frame
  888. * The browser element that holds the child process.
  889. * @param function [onDestroy]
  890. * Optional function to invoke when the child process closes
  891. * or the connection shuts down. (Need to forget about the
  892. * related TabActor)
  893. * @return object
  894. * A promise object that is resolved once the connection is
  895. * established.
  896. */
  897. connectToChild(connection, frame, onDestroy) {
  898. let deferred = SyncPromise.defer();
  899. // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
  900. // or else fallback to asking the frameLoader itself.
  901. let mm = frame.messageManager || frame.frameLoader.messageManager;
  902. mm.loadFrameScript("resource://devtools/server/child.js", false);
  903. let trackMessageManager = () => {
  904. frame.addEventListener("DevTools:BrowserSwap", onBrowserSwap);
  905. mm.addMessageListener("debug:setup-in-parent", onSetupInParent);
  906. if (!actor) {
  907. mm.addMessageListener("debug:actor", onActorCreated);
  908. }
  909. DebuggerServer._childMessageManagers.add(mm);
  910. };
  911. let untrackMessageManager = () => {
  912. frame.removeEventListener("DevTools:BrowserSwap", onBrowserSwap);
  913. mm.removeMessageListener("debug:setup-in-parent", onSetupInParent);
  914. if (!actor) {
  915. mm.removeMessageListener("debug:actor", onActorCreated);
  916. }
  917. DebuggerServer._childMessageManagers.delete(mm);
  918. };
  919. let actor, childTransport;
  920. let prefix = connection.allocID("child");
  921. // Compute the same prefix that's used by DebuggerServerConnection
  922. let connPrefix = prefix + "/";
  923. // provides hook to actor modules that need to exchange messages
  924. // between e10s parent and child processes
  925. let parentModules = [];
  926. let onSetupInParent = function (msg) {
  927. // We may have multiple connectToChild instance running for the same tab
  928. // and need to filter the messages.
  929. if (msg.json.prefix != connPrefix) {
  930. return false;
  931. }
  932. let { module, setupParent } = msg.json;
  933. let m;
  934. try {
  935. m = require(module);
  936. if (!(setupParent in m)) {
  937. dumpn(`ERROR: module '${module}' does not export '${setupParent}'`);
  938. return false;
  939. }
  940. parentModules.push(m[setupParent]({ mm, prefix: connPrefix }));
  941. return true;
  942. } catch (e) {
  943. let errorMessage =
  944. "Exception during actor module setup running in the parent process: ";
  945. DevToolsUtils.reportException(errorMessage + e);
  946. dumpn(`ERROR: ${errorMessage}\n\t module: '${module}'\n\t ` +
  947. `setupParent: '${setupParent}'\n${DevToolsUtils.safeErrorString(e)}`);
  948. return false;
  949. }
  950. };
  951. let onActorCreated = DevToolsUtils.makeInfallible(function (msg) {
  952. if (msg.json.prefix != prefix) {
  953. return;
  954. }
  955. mm.removeMessageListener("debug:actor", onActorCreated);
  956. // Pipe Debugger message from/to parent/child via the message manager
  957. childTransport = new ChildDebuggerTransport(mm, prefix);
  958. childTransport.hooks = {
  959. onPacket: connection.send.bind(connection),
  960. onClosed() {}
  961. };
  962. childTransport.ready();
  963. connection.setForwarding(prefix, childTransport);
  964. dumpn("establishing forwarding for app with prefix " + prefix);
  965. actor = msg.json.actor;
  966. deferred.resolve(actor);
  967. }).bind(this);
  968. // Listen for browser frame swap
  969. let onBrowserSwap = ({ detail: newFrame }) => {
  970. // Remove listeners from old frame and mm
  971. untrackMessageManager();
  972. // Update frame and mm to point to the new browser frame
  973. frame = newFrame;
  974. // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
  975. // or else fallback to asking the frameLoader itself.
  976. mm = frame.messageManager || frame.frameLoader.messageManager;
  977. // Add listeners to new frame and mm
  978. trackMessageManager();
  979. // provides hook to actor modules that need to exchange messages
  980. // between e10s parent and child processes
  981. parentModules.forEach(mod => {
  982. if (mod.onBrowserSwap) {
  983. mod.onBrowserSwap(mm);
  984. }
  985. });
  986. if (childTransport) {
  987. childTransport.swapBrowser(mm);
  988. }
  989. };
  990. let destroy = DevToolsUtils.makeInfallible(function () {
  991. // provides hook to actor modules that need to exchange messages
  992. // between e10s parent and child processes
  993. parentModules.forEach(mod => {
  994. if (mod.onDisconnected) {
  995. mod.onDisconnected();
  996. }
  997. });
  998. // TODO: Remove this deprecated path once it's no longer needed by add-ons.
  999. DebuggerServer.emit("disconnected-from-child:" + connPrefix,
  1000. { mm, prefix: connPrefix });
  1001. if (childTransport) {
  1002. // If we have a child transport, the actor has already
  1003. // been created. We need to stop using this message manager.
  1004. childTransport.close();
  1005. childTransport = null;
  1006. connection.cancelForwarding(prefix);
  1007. // ... and notify the child process to clean the tab actors.
  1008. try {
  1009. // Bug 1169643: Ignore any exception as the child process
  1010. // may already be destroyed by now.
  1011. mm.sendAsyncMessage("debug:disconnect", { prefix });
  1012. } catch (e) {
  1013. // Nothing to do
  1014. }
  1015. } else {
  1016. // Otherwise, the app has been closed before the actor
  1017. // had a chance to be created, so we are not able to create
  1018. // the actor.
  1019. deferred.resolve(null);
  1020. }
  1021. if (actor) {
  1022. // The ContentActor within the child process doesn't necessary
  1023. // have time to uninitialize itself when the app is closed/killed.
  1024. // So ensure telling the client that the related actor is detached.
  1025. connection.send({ from: actor.actor, type: "tabDetached" });
  1026. actor = null;
  1027. }
  1028. if (onDestroy) {
  1029. onDestroy(mm);
  1030. }
  1031. // Cleanup all listeners
  1032. untrackMessageManager();
  1033. Services.obs.removeObserver(onMessageManagerClose, "message-manager-close");
  1034. events.off(connection, "closed", destroy);
  1035. });
  1036. // Listen for various messages and frame events
  1037. trackMessageManager();
  1038. // Listen for app process exit
  1039. let onMessageManagerClose = function (subject, topic, data) {
  1040. if (subject == mm) {
  1041. destroy();
  1042. }
  1043. };
  1044. Services.obs.addObserver(onMessageManagerClose,
  1045. "message-manager-close", false);
  1046. // Listen for connection close to cleanup things
  1047. // when user unplug the device or we lose the connection somehow.
  1048. events.on(connection, "closed", destroy);
  1049. mm.sendAsyncMessage("debug:connect", { prefix });
  1050. return deferred.promise;
  1051. },
  1052. /**
  1053. * Create a new debugger connection for the given transport. Called after
  1054. * connectPipe(), from connectToParent, or from an incoming socket
  1055. * connection handler.
  1056. *
  1057. * If present, |forwardingPrefix| is a forwarding prefix that a parent
  1058. * server is using to recognizes messages intended for this server. Ensure
  1059. * that all our actors have names beginning with |forwardingPrefix + '/'|.
  1060. * In particular, the root actor's name will be |forwardingPrefix + '/root'|.
  1061. */
  1062. _onConnection(transport, forwardingPrefix, noRootActor = false) {
  1063. let connID;
  1064. if (forwardingPrefix) {
  1065. connID = forwardingPrefix + "/";
  1066. } else {
  1067. // Multiple servers can be started at the same time, and when that's the
  1068. // case, they are loaded in separate devtools loaders.
  1069. // So, use the current loader ID to prefix the connection ID and make it
  1070. // unique.
  1071. connID = "server" + loader.id + ".conn" + this._nextConnID++ + ".";
  1072. }
  1073. let conn = new DebuggerServerConnection(connID, transport);
  1074. this._connections[connID] = conn;
  1075. // Create a root actor for the connection and send the hello packet.
  1076. if (!noRootActor) {
  1077. conn.rootActor = this.createRootActor(conn);
  1078. if (forwardingPrefix) {
  1079. conn.rootActor.actorID = forwardingPrefix + "/root";
  1080. } else {
  1081. conn.rootActor.actorID = "root";
  1082. }
  1083. conn.addActor(conn.rootActor);
  1084. transport.send(conn.rootActor.sayHello());
  1085. }
  1086. transport.ready();
  1087. this.emit("connectionchange", "opened", conn);
  1088. return conn;
  1089. },
  1090. /**
  1091. * Remove the connection from the debugging server.
  1092. */
  1093. _connectionClosed(connection) {
  1094. delete this._connections[connection.prefix];
  1095. this.emit("connectionchange", "closed", connection);
  1096. },
  1097. // DebuggerServer extension API.
  1098. setRootActor(actorFactory) {
  1099. this.createRootActor = actorFactory;
  1100. },
  1101. /**
  1102. * Registers handlers for new tab-scoped request types defined dynamically.
  1103. * This is used for example by add-ons to augment the functionality of the tab
  1104. * actor. Note that the name or actorPrefix of the request type is not allowed
  1105. * to clash with existing protocol packet properties, like 'title', 'url' or
  1106. * 'actor', since that would break the protocol.
  1107. *
  1108. * @param actor function, object
  1109. * In case of function:
  1110. * The constructor function for this request type. This expects to be
  1111. * called as a constructor (i.e. with 'new'), and passed two
  1112. * arguments: the DebuggerServerConnection, and the BrowserTabActor
  1113. * with which it will be associated.
  1114. * Only used for deprecated eagerly loaded actors.
  1115. * In case of object:
  1116. * First argument of RegisteredActorFactory constructor.
  1117. * See the it's definition for more info.
  1118. *
  1119. * @param name string [optional]
  1120. * The name of the new request type. If this is not present, the
  1121. * actorPrefix property of the constructor prototype is used.
  1122. */
  1123. addTabActor(actor, name = actor.prototype.actorPrefix) {
  1124. if (["title", "url", "actor"].indexOf(name) != -1) {
  1125. throw Error(name + " is not allowed");
  1126. }
  1127. if (DebuggerServer.tabActorFactories.hasOwnProperty(name)) {
  1128. throw Error(name + " already exists");
  1129. }
  1130. DebuggerServer.tabActorFactories[name] = new RegisteredActorFactory(actor, name);
  1131. },
  1132. /**
  1133. * Unregisters the handler for the specified tab-scoped request type.
  1134. * This may be used for example by add-ons when shutting down or upgrading.
  1135. * When unregistering an existing tab actor remove related tab factory
  1136. * as well as all existing instances of the actor.
  1137. *
  1138. * @param actor function, object
  1139. * In case of function:
  1140. * The constructor function for this request type.
  1141. * In case of object:
  1142. * Same object being given to related addTabActor call.
  1143. */
  1144. removeTabActor(actor) {
  1145. for (let name in DebuggerServer.tabActorFactories) {
  1146. let handler = DebuggerServer.tabActorFactories[name];
  1147. if ((handler.name && handler.name == actor.name) ||
  1148. (handler.id && handler.id == actor.id)) {
  1149. delete DebuggerServer.tabActorFactories[name];
  1150. for (let connID of Object.getOwnPropertyNames(this._connections)) {
  1151. // DebuggerServerConnection in child process don't have rootActor
  1152. if (this._connections[connID].rootActor) {
  1153. this._connections[connID].rootActor.removeActorByName(name);
  1154. }
  1155. }
  1156. }
  1157. }
  1158. },
  1159. /**
  1160. * Registers handlers for new browser-scoped request types defined
  1161. * dynamically. This is used for example by add-ons to augment the
  1162. * functionality of the root actor. Note that the name or actorPrefix of the
  1163. * request type is not allowed to clash with existing protocol packet
  1164. * properties, like 'from', 'tabs' or 'selected', since that would break the
  1165. * protocol.
  1166. *
  1167. * @param actor function, object
  1168. * In case of function:
  1169. * The constructor function for this request type. This expects to be
  1170. * called as a constructor (i.e. with 'new'), and passed two
  1171. * arguments: the DebuggerServerConnection, and the BrowserRootActor
  1172. * with which it will be associated.
  1173. * Only used for deprecated eagerly loaded actors.
  1174. * In case of object:
  1175. * First argument of RegisteredActorFactory constructor.
  1176. * See the it's definition for more info.
  1177. *
  1178. * @param name string [optional]
  1179. * The name of the new request type. If this is not present, the
  1180. * actorPrefix property of the constructor prototype is used.
  1181. */
  1182. addGlobalActor(actor, name = actor.prototype.actorPrefix) {
  1183. if (["from", "tabs", "selected"].indexOf(name) != -1) {
  1184. throw Error(name + " is not allowed");
  1185. }
  1186. if (DebuggerServer.globalActorFactories.hasOwnProperty(name)) {
  1187. throw Error(name + " already exists");
  1188. }
  1189. DebuggerServer.globalActorFactories[name] = new RegisteredActorFactory(actor, name);
  1190. },
  1191. /**
  1192. * Unregisters the handler for the specified browser-scoped request type.
  1193. * This may be used for example by add-ons when shutting down or upgrading.
  1194. * When unregistering an existing global actor remove related global factory
  1195. * as well as all existing instances of the actor.
  1196. *
  1197. * @param actor function, object
  1198. * In case of function:
  1199. * The constructor function for this request type.
  1200. * In case of object:
  1201. * Same object being given to related addGlobalActor call.
  1202. */
  1203. removeGlobalActor(actor) {
  1204. for (let name in DebuggerServer.globalActorFactories) {
  1205. let handler = DebuggerServer.globalActorFactories[name];
  1206. if ((handler.name && handler.name == actor.name) ||
  1207. (handler.id && handler.id == actor.id)) {
  1208. delete DebuggerServer.globalActorFactories[name];
  1209. for (let connID of Object.getOwnPropertyNames(this._connections)) {
  1210. this._connections[connID].rootActor.removeActorByName(name);
  1211. }
  1212. }
  1213. }
  1214. },
  1215. /**
  1216. * ⚠ TESTING ONLY! ⚠ Searches all active connections for an actor matching an ID.
  1217. * This is helpful for some tests which depend on reaching into the server to check some
  1218. * properties of an actor.
  1219. */
  1220. _searchAllConnectionsForActor(actorID) {
  1221. for (let connID of Object.getOwnPropertyNames(this._connections)) {
  1222. let actor = this._connections[connID].getActor(actorID);
  1223. if (actor) {
  1224. return actor;
  1225. }
  1226. }
  1227. return null;
  1228. },
  1229. };
  1230. // Expose these to save callers the trouble of importing DebuggerSocket
  1231. DevToolsUtils.defineLazyGetter(DebuggerServer, "Authenticators", () => {
  1232. return Authentication.Authenticators;
  1233. });
  1234. DevToolsUtils.defineLazyGetter(DebuggerServer, "AuthenticationResult", () => {
  1235. return Authentication.AuthenticationResult;
  1236. });
  1237. EventEmitter.decorate(DebuggerServer);
  1238. if (this.exports) {
  1239. exports.DebuggerServer = DebuggerServer;
  1240. exports.ActorPool = ActorPool;
  1241. exports.OriginalLocation = OriginalLocation;
  1242. }
  1243. // Needed on B2G (See header note)
  1244. this.DebuggerServer = DebuggerServer;
  1245. this.ActorPool = ActorPool;
  1246. this.OriginalLocation = OriginalLocation;
  1247. // When using DebuggerServer.addActors, some symbols are expected to be in
  1248. // the scope of the added actor even before the corresponding modules are
  1249. // loaded, so let's explicitly bind the expected symbols here.
  1250. var includes = ["Components", "Ci", "Cu", "require", "Services", "DebuggerServer",
  1251. "ActorPool", "DevToolsUtils"];
  1252. includes.forEach(name => {
  1253. DebuggerServer[name] = this[name];
  1254. });
  1255. /**
  1256. * Creates a DebuggerServerConnection.
  1257. *
  1258. * Represents a connection to this debugging global from a client.
  1259. * Manages a set of actors and actor pools, allocates actor ids, and
  1260. * handles incoming requests.
  1261. *
  1262. * @param prefix string
  1263. * All actor IDs created by this connection should be prefixed
  1264. * with prefix.
  1265. * @param transport transport
  1266. * Packet transport for the debugging protocol.
  1267. */
  1268. function DebuggerServerConnection(prefix, transport) {
  1269. this._prefix = prefix;
  1270. this._transport = transport;
  1271. this._transport.hooks = this;
  1272. this._nextID = 1;
  1273. this._actorPool = new ActorPool(this);
  1274. this._extraPools = [this._actorPool];
  1275. // Responses to a given actor must be returned the the client
  1276. // in the same order as the requests that they're replying to, but
  1277. // Implementations might finish serving requests in a different
  1278. // order. To keep things in order we generate a promise for each
  1279. // request, chained to the promise for the request before it.
  1280. // This map stores the latest request promise in the chain, keyed
  1281. // by an actor ID string.
  1282. this._actorResponses = new Map();
  1283. /*
  1284. * We can forward packets to other servers, if the actors on that server
  1285. * all use a distinct prefix on their names. This is a map from prefixes
  1286. * to transports: it maps a prefix P to a transport T if T conveys
  1287. * packets to the server whose actors' names all begin with P + "/".
  1288. */
  1289. this._forwardingPrefixes = new Map();
  1290. }
  1291. DebuggerServerConnection.prototype = {
  1292. _prefix: null,
  1293. get prefix() {
  1294. return this._prefix;
  1295. },
  1296. _transport: null,
  1297. get transport() {
  1298. return this._transport;
  1299. },
  1300. /**
  1301. * Message manager used to communicate with the parent process,
  1302. * set by child.js. Is only defined for connections instantiated
  1303. * within a child process.
  1304. */
  1305. parentMessageManager: null,
  1306. close() {
  1307. if (this._transport) {
  1308. this._transport.close();
  1309. }
  1310. },
  1311. send(packet) {
  1312. this.transport.send(packet);
  1313. },
  1314. /**
  1315. * Used when sending a bulk reply from an actor.
  1316. * @see DebuggerTransport.prototype.startBulkSend
  1317. */
  1318. startBulkSend(header) {
  1319. return this.transport.startBulkSend(header);
  1320. },
  1321. allocID(prefix) {
  1322. return this.prefix + (prefix || "") + this._nextID++;
  1323. },
  1324. /**
  1325. * Add a map of actor IDs to the connection.
  1326. */
  1327. addActorPool(actorPool) {
  1328. this._extraPools.push(actorPool);
  1329. },
  1330. /**
  1331. * Remove a previously-added pool of actors to the connection.
  1332. *
  1333. * @param ActorPool actorPool
  1334. * The ActorPool instance you want to remove.
  1335. * @param boolean noCleanup [optional]
  1336. * True if you don't want to disconnect each actor from the pool, false
  1337. * otherwise.
  1338. */
  1339. removeActorPool(actorPool, noCleanup) {
  1340. // When a connection is closed, it removes each of its actor pools. When an
  1341. // actor pool is removed, it calls the disconnect method on each of its
  1342. // actors. Some actors, such as ThreadActor, manage their own actor pools.
  1343. // When the disconnect method is called on these actors, they manually
  1344. // remove their actor pools. Consequently, this method is reentrant.
  1345. //
  1346. // In addition, some actors, such as ThreadActor, perform asynchronous work
  1347. // (in the case of ThreadActor, because they need to resume), before they
  1348. // remove each of their actor pools. Since we don't wait for this work to
  1349. // be completed, we can end up in this function recursively after the
  1350. // connection already set this._extraPools to null.
  1351. //
  1352. // This is a bug: if the disconnect method can perform asynchronous work,
  1353. // then we should wait for that work to be completed before setting this.
  1354. // _extraPools to null. As a temporary solution, it should be acceptable
  1355. // to just return early (if this._extraPools has been set to null, all
  1356. // actors pools for this connection should already have been removed).
  1357. if (this._extraPools === null) {
  1358. return;
  1359. }
  1360. let index = this._extraPools.lastIndexOf(actorPool);
  1361. if (index > -1) {
  1362. let pool = this._extraPools.splice(index, 1);
  1363. if (!noCleanup) {
  1364. pool.forEach(p => p.destroy());
  1365. }
  1366. }
  1367. },
  1368. /**
  1369. * Add an actor to the default actor pool for this connection.
  1370. */
  1371. addActor(actor) {
  1372. this._actorPool.addActor(actor);
  1373. },
  1374. /**
  1375. * Remove an actor to the default actor pool for this connection.
  1376. */
  1377. removeActor(actor) {
  1378. this._actorPool.removeActor(actor);
  1379. },
  1380. /**
  1381. * Match the api expected by the protocol library.
  1382. */
  1383. unmanage(actor) {
  1384. return this.removeActor(actor);
  1385. },
  1386. /**
  1387. * Look up an actor implementation for an actorID. Will search
  1388. * all the actor pools registered with the connection.
  1389. *
  1390. * @param actorID string
  1391. * Actor ID to look up.
  1392. */
  1393. getActor(actorID) {
  1394. let pool = this.poolFor(actorID);
  1395. if (pool) {
  1396. return pool.get(actorID);
  1397. }
  1398. if (actorID === "root") {
  1399. return this.rootActor;
  1400. }
  1401. return null;
  1402. },
  1403. _getOrCreateActor(actorID) {
  1404. let actor = this.getActor(actorID);
  1405. if (!actor) {
  1406. this.transport.send({ from: actorID ? actorID : "root",
  1407. error: "noSuchActor",
  1408. message: "No such actor for ID: " + actorID });
  1409. return null;
  1410. }
  1411. // Dynamically-loaded actors have to be created lazily.
  1412. if (actor instanceof ObservedActorFactory) {
  1413. try {
  1414. actor = actor.createActor();
  1415. } catch (e) {
  1416. this.transport.send(this._unknownError(
  1417. "Error occurred while creating actor '" + actor.name,
  1418. e));
  1419. }
  1420. } else if (typeof (actor) !== "object") {
  1421. // ActorPools should now contain only actor instances (i.e. objects)
  1422. // or ObservedActorFactory instances.
  1423. throw new Error("Unexpected actor constructor/function in ActorPool " +
  1424. "for actorID=" + actorID + ".");
  1425. }
  1426. return actor;
  1427. },
  1428. poolFor(actorID) {
  1429. for (let pool of this._extraPools) {
  1430. if (pool.has(actorID)) {
  1431. return pool;
  1432. }
  1433. }
  1434. return null;
  1435. },
  1436. _unknownError(prefix, error) {
  1437. let errorString = prefix + ": " + DevToolsUtils.safeErrorString(error);
  1438. reportError(errorString);
  1439. dumpn(errorString);
  1440. return {
  1441. error: "unknownError",
  1442. message: errorString
  1443. };
  1444. },
  1445. _queueResponse: function (from, type, responseOrPromise) {
  1446. let pendingResponse = this._actorResponses.get(from) || SyncPromise.resolve(null);
  1447. let responsePromise = pendingResponse.then(() => {
  1448. return responseOrPromise;
  1449. }).then(response => {
  1450. if (!response.from) {
  1451. response.from = from;
  1452. }
  1453. this.transport.send(response);
  1454. }).then(null, (e) => {
  1455. let errorPacket = this._unknownError(
  1456. "error occurred while processing '" + type, e);
  1457. errorPacket.from = from;
  1458. this.transport.send(errorPacket);
  1459. });
  1460. this._actorResponses.set(from, responsePromise);
  1461. },
  1462. /**
  1463. * Passes a set of options to the BrowserAddonActors for the given ID.
  1464. *
  1465. * @param id string
  1466. * The ID of the add-on to pass the options to
  1467. * @param options object
  1468. * The options.
  1469. * @return a promise that will be resolved when complete.
  1470. */
  1471. setAddonOptions(id, options) {
  1472. let addonList = this.rootActor._parameters.addonList;
  1473. if (!addonList) {
  1474. return SyncPromise.resolve();
  1475. }
  1476. return addonList.getList().then((addonActors) => {
  1477. for (let actor of addonActors) {
  1478. if (actor.id != id) {
  1479. continue;
  1480. }
  1481. actor.setOptions(options);
  1482. return;
  1483. }
  1484. });
  1485. },
  1486. /* Forwarding packets to other transports based on actor name prefixes. */
  1487. /*
  1488. * Arrange to forward packets to another server. This is how we
  1489. * forward debugging connections to child processes.
  1490. *
  1491. * If we receive a packet for an actor whose name begins with |prefix|
  1492. * followed by '/', then we will forward that packet to |transport|.
  1493. *
  1494. * This overrides any prior forwarding for |prefix|.
  1495. *
  1496. * @param prefix string
  1497. * The actor name prefix, not including the '/'.
  1498. * @param transport object
  1499. * A packet transport to which we should forward packets to actors
  1500. * whose names begin with |(prefix + '/').|
  1501. */
  1502. setForwarding(prefix, transport) {
  1503. this._forwardingPrefixes.set(prefix, transport);
  1504. },
  1505. /*
  1506. * Stop forwarding messages to actors whose names begin with
  1507. * |prefix+'/'|. Such messages will now elicit 'noSuchActor' errors.
  1508. */
  1509. cancelForwarding(prefix) {
  1510. this._forwardingPrefixes.delete(prefix);
  1511. // Notify the client that forwarding in now cancelled for this prefix.
  1512. // There could be requests in progress that the client should abort rather leaving
  1513. // handing indefinitely.
  1514. if (this.rootActor) {
  1515. this.send(this.rootActor.forwardingCancelled(prefix));
  1516. }
  1517. },
  1518. sendActorEvent(actorID, eventName, event = {}) {
  1519. event.from = actorID;
  1520. event.type = eventName;
  1521. this.send(event);
  1522. },
  1523. // Transport hooks.
  1524. /**
  1525. * Called by DebuggerTransport to dispatch incoming packets as appropriate.
  1526. *
  1527. * @param packet object
  1528. * The incoming packet.
  1529. */
  1530. onPacket(packet) {
  1531. // If the actor's name begins with a prefix we've been asked to
  1532. // forward, do so.
  1533. //
  1534. // Note that the presence of a prefix alone doesn't indicate that
  1535. // forwarding is needed: in DebuggerServerConnection instances in child
  1536. // processes, every actor has a prefixed name.
  1537. if (this._forwardingPrefixes.size > 0) {
  1538. let to = packet.to;
  1539. let separator = to.lastIndexOf("/");
  1540. while (separator >= 0) {
  1541. to = to.substring(0, separator);
  1542. let forwardTo = this._forwardingPrefixes.get(packet.to.substring(0, separator));
  1543. if (forwardTo) {
  1544. forwardTo.send(packet);
  1545. return;
  1546. }
  1547. separator = to.lastIndexOf("/");
  1548. }
  1549. }
  1550. let actor = this._getOrCreateActor(packet.to);
  1551. if (!actor) {
  1552. return;
  1553. }
  1554. let ret = null;
  1555. // handle "requestTypes" RDP request.
  1556. if (packet.type == "requestTypes") {
  1557. ret = { from: actor.actorID, requestTypes: Object.keys(actor.requestTypes) };
  1558. } else if (actor.requestTypes && actor.requestTypes[packet.type]) {
  1559. // Dispatch the request to the actor.
  1560. try {
  1561. this.currentPacket = packet;
  1562. ret = actor.requestTypes[packet.type].bind(actor)(packet, this);
  1563. } catch (e) {
  1564. this.transport.send(this._unknownError(
  1565. "error occurred while processing '" + packet.type,
  1566. e));
  1567. } finally {
  1568. this.currentPacket = undefined;
  1569. }
  1570. } else {
  1571. ret = { error: "unrecognizedPacketType",
  1572. message: ("Actor " + actor.actorID +
  1573. " does not recognize the packet type " +
  1574. packet.type) };
  1575. }
  1576. // There will not be a return value if a bulk reply is sent.
  1577. if (ret) {
  1578. this._queueResponse(packet.to, packet.type, ret);
  1579. }
  1580. },
  1581. /**
  1582. * Called by the DebuggerTransport to dispatch incoming bulk packets as
  1583. * appropriate.
  1584. *
  1585. * @param packet object
  1586. * The incoming packet, which contains:
  1587. * * actor: Name of actor that will receive the packet
  1588. * * type: Name of actor's method that should be called on receipt
  1589. * * length: Size of the data to be read
  1590. * * stream: This input stream should only be used directly if you can
  1591. * ensure that you will read exactly |length| bytes and will
  1592. * not close the stream when reading is complete
  1593. * * done: If you use the stream directly (instead of |copyTo|
  1594. * below), you must signal completion by resolving /
  1595. * rejecting this deferred. If it's rejected, the transport
  1596. * will be closed. If an Error is supplied as a rejection
  1597. * value, it will be logged via |dumpn|. If you do use
  1598. * |copyTo|, resolving is taken care of for you when copying
  1599. * completes.
  1600. * * copyTo: A helper function for getting your data out of the stream
  1601. * that meets the stream handling requirements above, and has
  1602. * the following signature:
  1603. * @param output nsIAsyncOutputStream
  1604. * The stream to copy to.
  1605. * @return Promise
  1606. * The promise is resolved when copying completes or rejected
  1607. * if any (unexpected) errors occur.
  1608. * This object also emits "progress" events for each chunk
  1609. * that is copied. See stream-utils.js.
  1610. */
  1611. onBulkPacket(packet) {
  1612. let { actor: actorKey, type } = packet;
  1613. let actor = this._getOrCreateActor(actorKey);
  1614. if (!actor) {
  1615. return;
  1616. }
  1617. // Dispatch the request to the actor.
  1618. let ret;
  1619. if (actor.requestTypes && actor.requestTypes[type]) {
  1620. try {
  1621. ret = actor.requestTypes[type].call(actor, packet);
  1622. } catch (e) {
  1623. this.transport.send(this._unknownError(
  1624. "error occurred while processing bulk packet '" + type, e));
  1625. packet.done.reject(e);
  1626. }
  1627. } else {
  1628. let message = "Actor " + actorKey +
  1629. " does not recognize the bulk packet type " + type;
  1630. ret = { error: "unrecognizedPacketType",
  1631. message: message };
  1632. packet.done.reject(new Error(message));
  1633. }
  1634. // If there is a JSON response, queue it for sending back to the client.
  1635. if (ret) {
  1636. this._queueResponse(actorKey, type, ret);
  1637. }
  1638. },
  1639. /**
  1640. * Called by DebuggerTransport when the underlying stream is closed.
  1641. *
  1642. * @param status nsresult
  1643. * The status code that corresponds to the reason for closing
  1644. * the stream.
  1645. */
  1646. onClosed(status) {
  1647. dumpn("Cleaning up connection.");
  1648. if (!this._actorPool) {
  1649. // Ignore this call if the connection is already closed.
  1650. return;
  1651. }
  1652. this._actorPool = null;
  1653. events.emit(this, "closed", status);
  1654. this._extraPools.forEach(p => p.destroy());
  1655. this._extraPools = null;
  1656. this.rootActor = null;
  1657. this._transport = null;
  1658. DebuggerServer._connectionClosed(this);
  1659. },
  1660. /*
  1661. * Debugging helper for inspecting the state of the actor pools.
  1662. */
  1663. _dumpPools() {
  1664. dumpn("/-------------------- dumping pools:");
  1665. if (this._actorPool) {
  1666. dumpn("--------------------- actorPool actors: " +
  1667. uneval(Object.keys(this._actorPool._actors)));
  1668. }
  1669. for (let pool of this._extraPools) {
  1670. if (pool !== this._actorPool) {
  1671. dumpn("--------------------- extraPool actors: " +
  1672. uneval(Object.keys(pool._actors)));
  1673. }
  1674. }
  1675. },
  1676. /*
  1677. * Debugging helper for inspecting the state of an actor pool.
  1678. */
  1679. _dumpPool(pool) {
  1680. dumpn("/-------------------- dumping pool:");
  1681. dumpn("--------------------- actorPool actors: " +
  1682. uneval(Object.keys(pool._actors)));
  1683. },
  1684. /**
  1685. * In a content child process, ask the DebuggerServer in the parent process
  1686. * to execute a given module setup helper.
  1687. *
  1688. * @param module
  1689. * The module to be required
  1690. * @param setupParent
  1691. * The name of the setup helper exported by the above module
  1692. * (setup helper signature: function ({mm}) { ... })
  1693. * @return boolean
  1694. * true if the setup helper returned successfully
  1695. */
  1696. setupInParent({ module, setupParent }) {
  1697. if (!this.parentMessageManager) {
  1698. return false;
  1699. }
  1700. let { sendSyncMessage } = this.parentMessageManager;
  1701. return sendSyncMessage("debug:setup-in-parent", {
  1702. prefix: this.prefix,
  1703. module: module,
  1704. setupParent: setupParent
  1705. });
  1706. },
  1707. };