browser_devtools_api.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
  2. /* Any copyright is dedicated to the Public Domain.
  3. * http://creativecommons.org/publicdomain/zero/1.0/ */
  4. //
  5. // Whitelisting this test.
  6. // As part of bug 1077403, the leaking uncaught rejections should be fixed.
  7. //
  8. thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.docShell is null");
  9. // When running in a standalone directory, we get this error
  10. thisTestLeaksUncaughtRejectionsAndShouldBeFixed("TypeError: this.doc is undefined");
  11. // Tests devtools API
  12. const toolId1 = "test-tool-1";
  13. const toolId2 = "test-tool-2";
  14. var EventEmitter = require("devtools/shared/event-emitter");
  15. function test() {
  16. addTab("about:blank").then(runTests1);
  17. }
  18. // Test scenario 1: the tool definition build method returns a promise.
  19. function runTests1(aTab) {
  20. let toolDefinition = {
  21. id: toolId1,
  22. isTargetSupported: () => true,
  23. visibilityswitch: "devtools.test-tool.enabled",
  24. url: "about:blank",
  25. label: "someLabel",
  26. build: function (iframeWindow, toolbox) {
  27. let panel = new DevToolPanel(iframeWindow, toolbox);
  28. return panel.open();
  29. },
  30. };
  31. ok(gDevTools, "gDevTools exists");
  32. ok(!gDevTools.getToolDefinitionMap().has(toolId1),
  33. "The tool is not registered");
  34. gDevTools.registerTool(toolDefinition);
  35. ok(gDevTools.getToolDefinitionMap().has(toolId1),
  36. "The tool is registered");
  37. let target = TargetFactory.forTab(gBrowser.selectedTab);
  38. let events = {};
  39. // Check events on the gDevTools and toolbox objects.
  40. gDevTools.once(toolId1 + "-init", (event, toolbox, iframe) => {
  41. ok(iframe, "iframe argument available");
  42. toolbox.once(toolId1 + "-init", (event, iframe) => {
  43. ok(iframe, "iframe argument available");
  44. events["init"] = true;
  45. });
  46. });
  47. gDevTools.once(toolId1 + "-ready", (event, toolbox, panel) => {
  48. ok(panel, "panel argument available");
  49. toolbox.once(toolId1 + "-ready", (event, panel) => {
  50. ok(panel, "panel argument available");
  51. events["ready"] = true;
  52. });
  53. });
  54. gDevTools.showToolbox(target, toolId1).then(function (toolbox) {
  55. is(toolbox.target, target, "toolbox target is correct");
  56. is(toolbox.target.tab, gBrowser.selectedTab, "targeted tab is correct");
  57. ok(events["init"], "init event fired");
  58. ok(events["ready"], "ready event fired");
  59. gDevTools.unregisterTool(toolId1);
  60. // Wait for unregisterTool to select the next tool before calling runTests2,
  61. // otherwise we will receive the wrong select event when waiting for
  62. // unregisterTool to select the next tool in continueTests below.
  63. toolbox.once("select", runTests2);
  64. });
  65. }
  66. // Test scenario 2: the tool definition build method returns panel instance.
  67. function runTests2() {
  68. let toolDefinition = {
  69. id: toolId2,
  70. isTargetSupported: () => true,
  71. visibilityswitch: "devtools.test-tool.enabled",
  72. url: "about:blank",
  73. label: "someLabel",
  74. build: function (iframeWindow, toolbox) {
  75. return new DevToolPanel(iframeWindow, toolbox);
  76. },
  77. };
  78. ok(!gDevTools.getToolDefinitionMap().has(toolId2),
  79. "The tool is not registered");
  80. gDevTools.registerTool(toolDefinition);
  81. ok(gDevTools.getToolDefinitionMap().has(toolId2),
  82. "The tool is registered");
  83. let target = TargetFactory.forTab(gBrowser.selectedTab);
  84. let events = {};
  85. // Check events on the gDevTools and toolbox objects.
  86. gDevTools.once(toolId2 + "-init", (event, toolbox, iframe) => {
  87. ok(iframe, "iframe argument available");
  88. toolbox.once(toolId2 + "-init", (event, iframe) => {
  89. ok(iframe, "iframe argument available");
  90. events["init"] = true;
  91. });
  92. });
  93. gDevTools.once(toolId2 + "-build", (event, toolbox, panel, iframe) => {
  94. ok(panel, "panel argument available");
  95. toolbox.once(toolId2 + "-build", (event, panel, iframe) => {
  96. ok(panel, "panel argument available");
  97. events["build"] = true;
  98. });
  99. });
  100. gDevTools.once(toolId2 + "-ready", (event, toolbox, panel) => {
  101. ok(panel, "panel argument available");
  102. toolbox.once(toolId2 + "-ready", (event, panel) => {
  103. ok(panel, "panel argument available");
  104. events["ready"] = true;
  105. });
  106. });
  107. gDevTools.showToolbox(target, toolId2).then(function (toolbox) {
  108. is(toolbox.target, target, "toolbox target is correct");
  109. is(toolbox.target.tab, gBrowser.selectedTab, "targeted tab is correct");
  110. ok(events["init"], "init event fired");
  111. ok(events["build"], "build event fired");
  112. ok(events["ready"], "ready event fired");
  113. continueTests(toolbox);
  114. });
  115. }
  116. var continueTests = Task.async(function* (toolbox, panel) {
  117. ok(toolbox.getCurrentPanel(), "panel value is correct");
  118. is(toolbox.currentToolId, toolId2, "toolbox _currentToolId is correct");
  119. ok(!toolbox.doc.getElementById("toolbox-tab-" + toolId2).hasAttribute("icon-invertable"),
  120. "The tool tab does not have the invertable attribute");
  121. ok(toolbox.doc.getElementById("toolbox-tab-inspector").hasAttribute("icon-invertable"),
  122. "The builtin tool tabs do have the invertable attribute");
  123. let toolDefinitions = gDevTools.getToolDefinitionMap();
  124. ok(toolDefinitions.has(toolId2), "The tool is in gDevTools");
  125. let toolDefinition = toolDefinitions.get(toolId2);
  126. is(toolDefinition.id, toolId2, "toolDefinition id is correct");
  127. info("Testing toolbox tool-unregistered event");
  128. let toolSelected = toolbox.once("select");
  129. let unregisteredTool = yield new Promise(resolve => {
  130. toolbox.once("tool-unregistered", (e, id) => resolve(id));
  131. gDevTools.unregisterTool(toolId2);
  132. });
  133. yield toolSelected;
  134. is(unregisteredTool, toolId2, "Event returns correct id");
  135. ok(!toolbox.isToolRegistered(toolId2),
  136. "Toolbox: The tool is not registered");
  137. ok(!gDevTools.getToolDefinitionMap().has(toolId2),
  138. "The tool is no longer registered");
  139. info("Testing toolbox tool-registered event");
  140. let registeredTool = yield new Promise(resolve => {
  141. toolbox.once("tool-registered", (e, id) => resolve(id));
  142. gDevTools.registerTool(toolDefinition);
  143. });
  144. is(registeredTool, toolId2, "Event returns correct id");
  145. ok(toolbox.isToolRegistered(toolId2),
  146. "Toolbox: The tool is registered");
  147. ok(gDevTools.getToolDefinitionMap().has(toolId2),
  148. "The tool is registered");
  149. info("Unregistering tool");
  150. gDevTools.unregisterTool(toolId2);
  151. destroyToolbox(toolbox);
  152. });
  153. function destroyToolbox(toolbox) {
  154. toolbox.destroy().then(function () {
  155. let target = TargetFactory.forTab(gBrowser.selectedTab);
  156. ok(gDevTools._toolboxes.get(target) == null, "gDevTools doesn't know about target");
  157. ok(toolbox.target == null, "toolbox doesn't know about target.");
  158. finishUp();
  159. });
  160. }
  161. function finishUp() {
  162. gBrowser.removeCurrentTab();
  163. finish();
  164. }
  165. /**
  166. * When a Toolbox is started it creates a DevToolPanel for each of the tools
  167. * by calling toolDefinition.build(). The returned object should
  168. * at least implement these functions. They will be used by the ToolBox.
  169. *
  170. * There may be no benefit in doing this as an abstract type, but if nothing
  171. * else gives us a place to write documentation.
  172. */
  173. function DevToolPanel(iframeWindow, toolbox) {
  174. EventEmitter.decorate(this);
  175. this._toolbox = toolbox;
  176. /* let doc = iframeWindow.document
  177. let label = doc.createElement("label");
  178. let textNode = doc.createTextNode("Some Tool");
  179. label.appendChild(textNode);
  180. doc.body.appendChild(label);*/
  181. }
  182. DevToolPanel.prototype = {
  183. open: function () {
  184. let deferred = defer();
  185. executeSoon(() => {
  186. this._isReady = true;
  187. this.emit("ready");
  188. deferred.resolve(this);
  189. });
  190. return deferred.promise;
  191. },
  192. get target() {
  193. return this._toolbox.target;
  194. },
  195. get toolbox() {
  196. return this._toolbox;
  197. },
  198. get isReady() {
  199. return this._isReady;
  200. },
  201. _isReady: false,
  202. destroy: function DTI_destroy() {
  203. return defer(null);
  204. },
  205. };