Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / devtools / client / definitions.js
blobe12c1c8dcc3f5b1ff717a08c140c90d6f59bc2b7
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/. */
5 "use strict";
7 const osString = Services.appinfo.OS;
9 // Panels
10 loader.lazyGetter(
11   this,
12   "OptionsPanel",
13   () =>
14     require("resource://devtools/client/framework/toolbox-options.js")
15       .OptionsPanel
17 loader.lazyGetter(
18   this,
19   "InspectorPanel",
20   () => require("resource://devtools/client/inspector/panel.js").InspectorPanel
22 loader.lazyGetter(
23   this,
24   "WebConsolePanel",
25   () =>
26     require("resource://devtools/client/webconsole/panel.js").WebConsolePanel
28 loader.lazyGetter(
29   this,
30   "DebuggerPanel",
31   () => require("resource://devtools/client/debugger/panel.js").DebuggerPanel
33 loader.lazyGetter(
34   this,
35   "StyleEditorPanel",
36   () =>
37     require("resource://devtools/client/styleeditor/panel.js").StyleEditorPanel
39 loader.lazyGetter(
40   this,
41   "MemoryPanel",
42   () => require("resource://devtools/client/memory/panel.js").MemoryPanel
44 loader.lazyGetter(
45   this,
46   "NewPerformancePanel",
47   () =>
48     require("resource://devtools/client/performance-new/panel/panel.js")
49       .PerformancePanel
51 loader.lazyGetter(
52   this,
53   "NetMonitorPanel",
54   () =>
55     require("resource://devtools/client/netmonitor/panel.js").NetMonitorPanel
57 loader.lazyGetter(
58   this,
59   "StoragePanel",
60   () => require("resource://devtools/client/storage/panel.js").StoragePanel
62 loader.lazyGetter(
63   this,
64   "DomPanel",
65   () => require("resource://devtools/client/dom/panel.js").DomPanel
67 loader.lazyGetter(
68   this,
69   "AccessibilityPanel",
70   () =>
71     require("resource://devtools/client/accessibility/panel.js")
72       .AccessibilityPanel
74 loader.lazyGetter(
75   this,
76   "ApplicationPanel",
77   () =>
78     require("resource://devtools/client/application/panel.js").ApplicationPanel
81 // Other dependencies
82 loader.lazyRequireGetter(
83   this,
84   "ResponsiveUIManager",
85   "resource://devtools/client/responsive/manager.js"
88 const lazy = {};
89 ChromeUtils.defineESModuleGetters(lazy, {
90   AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
91 });
92 loader.lazyRequireGetter(
93   this,
94   "DevToolsExperimentalPrefs",
95   "resource://devtools/client/devtools-experimental-prefs.js"
97 loader.lazyRequireGetter(
98   this,
99   "captureAndSaveScreenshot",
100   "resource://devtools/client/shared/screenshot.js",
101   true
104 const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
105 const L10N = new LocalizationHelper(
106   "devtools/client/locales/startup.properties"
108 const CommandKeys = new Localization(
109   ["devtools/startup/key-shortcuts.ftl"],
110   true
113 var Tools = {};
114 exports.Tools = Tools;
116 // Definitions
117 Tools.options = {
118   id: "options",
119   ordinal: 0,
120   url: "chrome://devtools/content/framework/toolbox-options.html",
121   icon: "chrome://devtools/skin/images/settings.svg",
122   bgTheme: "theme-body",
123   label: l10n("options.label"),
124   iconOnly: true,
125   panelLabel: l10n("options.panelLabel"),
126   tooltip: l10n("optionsButton.tooltip"),
127   inMenu: false,
129   isToolSupported() {
130     return true;
131   },
133   build(iframeWindow, toolbox, commands) {
134     return new OptionsPanel(iframeWindow, toolbox, commands);
135   },
138 Tools.inspector = {
139   id: "inspector",
140   accesskey: l10n("inspector.accesskey"),
141   ordinal: 1,
142   icon: "chrome://devtools/skin/images/tool-inspector.svg",
143   url: "chrome://devtools/content/inspector/index.xhtml",
144   label: l10n("inspector.label"),
145   panelLabel: l10n("inspector.panelLabel"),
146   get tooltip() {
147     const key = commandkey("devtools-commandkey-inspector");
148     if (osString == "Darwin") {
149       const cmdShiftC = "Cmd+Shift+" + key;
150       const cmdOptC = "Cmd+Opt+" + key;
151       return l10n("inspector.mac.tooltip", cmdShiftC, cmdOptC);
152     }
154     const ctrlShiftC = "Ctrl+Shift+" + key;
155     return l10n("inspector.tooltip2", ctrlShiftC);
156   },
157   inMenu: false,
159   preventClosingOnKey: true,
160   // preventRaisingOnKey is used to keep the focus on the content window for shortcuts
161   // that trigger the element picker.
162   preventRaisingOnKey: true,
163   onkey(panel, toolbox) {
164     if (
165       Services.prefs.getBoolPref("devtools.command-button-pick.enabled", false)
166     ) {
167       toolbox.nodePicker.togglePicker();
168     }
169   },
171   isToolSupported(toolbox) {
172     return toolbox.target.hasActor("inspector");
173   },
175   build(iframeWindow, toolbox, commands) {
176     return new InspectorPanel(iframeWindow, toolbox, commands);
177   },
179 Tools.webConsole = {
180   id: "webconsole",
181   accesskey: l10n("webConsoleCmd.accesskey"),
182   ordinal: 2,
183   url: "chrome://devtools/content/webconsole/index.html",
184   icon: "chrome://devtools/skin/images/tool-webconsole.svg",
185   label: l10n("ToolboxTabWebconsole.label"),
186   menuLabel: l10n("MenuWebconsole.label"),
187   panelLabel: l10n("ToolboxWebConsole.panelLabel"),
188   get tooltip() {
189     return l10n(
190       "ToolboxWebconsole.tooltip2",
191       (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
192         commandkey("devtools-commandkey-webconsole")
193     );
194   },
195   inMenu: false,
197   preventClosingOnKey: true,
198   onkey(panel, toolbox) {
199     if (toolbox.splitConsole) {
200       return toolbox.focusConsoleInput();
201     }
203     panel.focusInput();
204     return undefined;
205   },
207   isToolSupported() {
208     return true;
209   },
210   build(iframeWindow, toolbox, commands) {
211     return new WebConsolePanel(iframeWindow, toolbox, commands);
212   },
215 Tools.jsdebugger = {
216   id: "jsdebugger",
217   accesskey: l10n("debuggerMenu.accesskey"),
218   ordinal: 3,
219   icon: "chrome://devtools/skin/images/tool-debugger.svg",
220   url: "chrome://devtools/content/debugger/index.html",
221   label: l10n("ToolboxDebugger.label"),
222   panelLabel: l10n("ToolboxDebugger.panelLabel"),
223   get tooltip() {
224     return l10n(
225       "ToolboxDebugger.tooltip4",
226       (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
227         commandkey("devtools-commandkey-jsdebugger")
228     );
229   },
230   inMenu: false,
231   isToolSupported() {
232     return true;
233   },
234   build(iframeWindow, toolbox, commands) {
235     return new DebuggerPanel(iframeWindow, toolbox, commands);
236   },
239 Tools.styleEditor = {
240   id: "styleeditor",
241   ordinal: 5,
242   visibilityswitch: "devtools.styleeditor.enabled",
243   accesskey: l10n("open.accesskey"),
244   icon: "chrome://devtools/skin/images/tool-styleeditor.svg",
245   url: "chrome://devtools/content/styleeditor/index.xhtml",
246   label: l10n("ToolboxStyleEditor.label"),
247   panelLabel: l10n("ToolboxStyleEditor.panelLabel"),
248   get tooltip() {
249     return l10n(
250       "ToolboxStyleEditor.tooltip3",
251       "Shift+" + functionkey(commandkey("devtools-commandkey-styleeditor"))
252     );
253   },
254   inMenu: false,
255   isToolSupported(toolbox) {
256     return toolbox.target.hasActor("styleSheets");
257   },
259   build(iframeWindow, toolbox, commands) {
260     return new StyleEditorPanel(iframeWindow, toolbox, commands);
261   },
264 Tools.performance = {
265   id: "performance",
266   ordinal: 6,
267   icon: "chrome://devtools/skin/images/tool-profiler.svg",
268   url: "chrome://devtools/content/performance-new/panel/index.xhtml",
269   visibilityswitch: "devtools.performance.enabled",
270   label: l10n("performance.label"),
271   panelLabel: l10n("performance.panelLabel"),
272   get tooltip() {
273     return l10n(
274       "performance.tooltip",
275       "Shift+" + functionkey(commandkey("devtools-commandkey-performance"))
276     );
277   },
278   accesskey: l10n("performance.accesskey"),
279   inMenu: false,
280   isToolSupported(toolbox) {
281     // Only use the new performance panel on local tab toolboxes, as they are guaranteed
282     // to have a performance actor.
283     // Remote tab toolboxes (eg about:devtools-toolbox from about:debugging) should not
284     // use the performance panel; about:debugging provides a "Profile performance" button
285     // which can be used instead, without having the overhead of starting a remote toolbox.
286     // Also accept the Browser Toolbox, so that we can profile its process via a second browser toolbox.
287     return (
288       toolbox.commands.descriptorFront.isLocalTab || toolbox.isBrowserToolbox
289     );
290   },
291   build(frame, toolbox, commands) {
292     return new NewPerformancePanel(frame, toolbox, commands);
293   },
296 Tools.memory = {
297   id: "memory",
298   ordinal: 7,
299   icon: "chrome://devtools/skin/images/tool-memory.svg",
300   url: "chrome://devtools/content/memory/index.xhtml",
301   visibilityswitch: "devtools.memory.enabled",
302   label: l10n("memory.label"),
303   panelLabel: l10n("memory.panelLabel"),
304   tooltip: l10n("memory.tooltip"),
306   isToolSupported(toolbox) {
307     const { descriptorFront } = toolbox.commands;
308     return (
309       !descriptorFront.isWebExtensionDescriptor &&
310       !descriptorFront.isWorkerDescriptor
311     );
312   },
314   build(frame, toolbox, commands) {
315     return new MemoryPanel(frame, toolbox, commands);
316   },
319 Tools.netMonitor = {
320   id: "netmonitor",
321   accesskey: l10n("netmonitor.accesskey"),
322   ordinal: 4,
323   visibilityswitch: "devtools.netmonitor.enabled",
324   icon: "chrome://devtools/skin/images/tool-network.svg",
325   url: "chrome://devtools/content/netmonitor/index.html",
326   label: l10n("netmonitor.label"),
327   panelLabel: l10n("netmonitor.panelLabel"),
328   get tooltip() {
329     return l10n(
330       "netmonitor.tooltip2",
331       (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
332         commandkey("devtools-commandkey-netmonitor")
333     );
334   },
335   inMenu: false,
337   isToolSupported(toolbox) {
338     return (
339       toolbox.target.getTrait("networkMonitor") &&
340       !toolbox.target.isWorkerTarget
341     );
342   },
344   build(iframeWindow, toolbox, commands) {
345     return new NetMonitorPanel(iframeWindow, toolbox, commands);
346   },
349 Tools.storage = {
350   id: "storage",
351   ordinal: 8,
352   accesskey: l10n("storage.accesskey"),
353   visibilityswitch: "devtools.storage.enabled",
354   icon: "chrome://devtools/skin/images/tool-storage.svg",
355   url: "chrome://devtools/content/storage/index.xhtml",
356   label: l10n("storage.label"),
357   menuLabel: l10n("storage.menuLabel"),
358   panelLabel: l10n("storage.panelLabel"),
359   get tooltip() {
360     return l10n(
361       "storage.tooltip3",
362       "Shift+" + functionkey(commandkey("devtools-commandkey-storage"))
363     );
364   },
365   inMenu: false,
367   isToolSupported(toolbox) {
368     const { descriptorFront } = toolbox.commands;
369     // Storage is available on all contexts debugging a BrowsingContext.
370     // As of today, this is all but worker toolboxes.
371     return (
372       descriptorFront.isTabDescriptor ||
373       descriptorFront.isParentProcessDescriptor ||
374       descriptorFront.isWebExtensionDescriptor
375     );
376   },
378   build(iframeWindow, toolbox, commands) {
379     return new StoragePanel(iframeWindow, toolbox, commands);
380   },
383 Tools.dom = {
384   id: "dom",
385   accesskey: l10n("dom.accesskey"),
386   ordinal: 11,
387   visibilityswitch: "devtools.dom.enabled",
388   icon: "chrome://devtools/skin/images/tool-dom.svg",
389   url: "chrome://devtools/content/dom/index.html",
390   label: l10n("dom.label"),
391   panelLabel: l10n("dom.panelLabel"),
392   get tooltip() {
393     return l10n(
394       "dom.tooltip",
395       (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
396         commandkey("devtools-commandkey-dom")
397     );
398   },
399   inMenu: false,
401   isToolSupported() {
402     return true;
403   },
405   build(iframeWindow, toolbox, commands) {
406     return new DomPanel(iframeWindow, toolbox, commands);
407   },
410 Tools.accessibility = {
411   id: "accessibility",
412   accesskey: l10n("accessibility.accesskey"),
413   ordinal: 9,
414   modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
415   visibilityswitch: "devtools.accessibility.enabled",
416   icon: "chrome://devtools/skin/images/tool-accessibility.svg",
417   url: "chrome://devtools/content/accessibility/index.html",
418   label: l10n("accessibility.label"),
419   panelLabel: l10n("accessibility.panelLabel"),
420   get tooltip() {
421     return l10n(
422       "accessibility.tooltip3",
423       "Shift+" +
424         functionkey(commandkey("devtools-commandkey-accessibility-f12"))
425     );
426   },
427   inMenu: false,
429   isToolSupported(toolbox) {
430     return toolbox.target.hasActor("accessibility");
431   },
433   build(iframeWindow, toolbox, commands) {
434     return new AccessibilityPanel(iframeWindow, toolbox, commands);
435   },
438 Tools.application = {
439   id: "application",
440   ordinal: 10,
441   visibilityswitch: "devtools.application.enabled",
442   icon: "chrome://devtools/skin/images/tool-application.svg",
443   url: "chrome://devtools/content/application/index.html",
444   label: l10n("application.label"),
445   panelLabel: l10n("application.panelLabel"),
446   tooltip: l10n("application.tooltip"),
447   inMenu: false,
449   isToolSupported(toolbox) {
450     return toolbox.target.hasActor("manifest");
451   },
453   build(iframeWindow, toolbox, commands) {
454     return new ApplicationPanel(iframeWindow, toolbox, commands);
455   },
458 var defaultTools = [
459   Tools.options,
460   Tools.webConsole,
461   Tools.inspector,
462   Tools.jsdebugger,
463   Tools.styleEditor,
464   Tools.performance,
465   Tools.netMonitor,
466   Tools.storage,
467   Tools.memory,
468   Tools.dom,
469   Tools.accessibility,
470   Tools.application,
473 exports.defaultTools = defaultTools;
475 Tools.darkTheme = {
476   id: "dark",
477   label: l10n("options.darkTheme.label2"),
478   ordinal: 1,
479   stylesheets: ["chrome://devtools/skin/dark-theme.css"],
480   classList: ["theme-dark"],
483 Tools.lightTheme = {
484   id: "light",
485   label: l10n("options.lightTheme.label2"),
486   ordinal: 2,
487   stylesheets: ["chrome://devtools/skin/light-theme.css"],
488   classList: ["theme-light"],
491 exports.defaultThemes = [Tools.darkTheme, Tools.lightTheme];
493 // List buttons that can be toggled to prevent adding prefs for
494 // addons that have manually inserted toolbarbuttons into DOM.
495 // (By default, supported target is only local tab)
496 exports.ToolboxButtons = [
497   {
498     id: "command-button-experimental-prefs",
499     description: "DevTools Experimental preferences",
500     isToolSupported: () => !lazy.AppConstants.MOZILLA_OFFICIAL,
501     onClick: (event, toolbox) => DevToolsExperimentalPrefs.showTooltip(toolbox),
502     isChecked: () => DevToolsExperimentalPrefs.isAnyPreferenceEnabled(),
503   },
504   {
505     id: "command-button-responsive",
506     description: l10n(
507       "toolbox.buttons.responsive",
508       osString == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"
509     ),
510     isToolSupported: toolbox => toolbox.commands.descriptorFront.isLocalTab,
511     onClick(event, toolbox) {
512       const { localTab } = toolbox.commands.descriptorFront;
513       const browserWindow = localTab.ownerDocument.defaultView;
514       ResponsiveUIManager.toggle(browserWindow, localTab, {
515         trigger: "toolbox",
516       });
517     },
518     isChecked(toolbox) {
519       const { localTab } = toolbox.commands.descriptorFront;
520       if (!localTab) {
521         return false;
522       }
523       return ResponsiveUIManager.isActiveForTab(localTab);
524     },
525     isToggle: true,
526     setup(toolbox, onChange) {
527       ResponsiveUIManager.on("on", onChange);
528       ResponsiveUIManager.on("off", onChange);
529     },
530     teardown(toolbox, onChange) {
531       ResponsiveUIManager.off("on", onChange);
532       ResponsiveUIManager.off("off", onChange);
533     },
534   },
535   {
536     id: "command-button-screenshot",
537     description: l10n("toolbox.buttons.screenshot"),
538     isToolSupported: toolbox => {
539       return (
540         // @backward-compat { version 87 } We need to check for the screenshot actor as well
541         // when connecting to older server that does not have the screenshotContentActor
542         toolbox.target.hasActor("screenshotContent") ||
543         toolbox.target.hasActor("screenshot")
544       );
545     },
546     async onClick(event, toolbox) {
547       // Special case for screenshot button to check for clipboard preference
548       const clipboardEnabled = Services.prefs.getBoolPref(
549         "devtools.screenshot.clipboard.enabled"
550       );
552       // When screenshot to clipboard is enabled disabling saving to file
553       const args = {
554         fullpage: true,
555         file: !clipboardEnabled,
556         clipboard: clipboardEnabled,
557       };
559       const messages = await captureAndSaveScreenshot(
560         toolbox.target,
561         toolbox.win,
562         args
563       );
564       const notificationBox = toolbox.getNotificationBox();
565       const priorityMap = {
566         error: notificationBox.PRIORITY_CRITICAL_HIGH,
567         warn: notificationBox.PRIORITY_WARNING_HIGH,
568       };
569       for (const { text, level } of messages) {
570         // captureAndSaveScreenshot returns "saved" messages, that indicate where the
571         // screenshot was saved. In regular toolbox, we don't want to display them as
572         // the download UI can be used to open them.
573         // But in the browser toolbox, we can't see the download UI, so we'll display the
574         // saved message so the user knows there the file was saved.
575         if (
576           !toolbox.isBrowserToolbox &&
577           level !== "warn" &&
578           level !== "error"
579         ) {
580           continue;
581         }
582         notificationBox.appendNotification(
583           text,
584           null,
585           null,
586           priorityMap[level] || notificationBox.PRIORITY_INFO_MEDIUM
587         );
588       }
589     },
590   },
591   createHighlightButton(
592     ["RulersHighlighter", "ViewportSizeHighlighter"],
593     "rulers"
594   ),
595   createHighlightButton(["MeasuringToolHighlighter"], "measure"),
598 function createHighlightButton(highlighters, id) {
599   return {
600     id: `command-button-${id}`,
601     description: l10n(`toolbox.buttons.${id}`),
602     isToolSupported: toolbox =>
603       toolbox.commands.descriptorFront.isTabDescriptor,
604     async onClick(event, toolbox) {
605       const inspectorFront = await toolbox.target.getFront("inspector");
607       await Promise.all(
608         highlighters.map(async name => {
609           const highlighter = await inspectorFront.getOrCreateHighlighterByType(
610             name
611           );
613           if (highlighter.isShown()) {
614             await highlighter.hide();
615           } else {
616             await highlighter.show();
617           }
618         })
619       );
620     },
621     isChecked(toolbox) {
622       // if the inspector doesn't exist, then the highlighter has not yet been connected
623       // to the front end.
624       const inspectorFront = toolbox.target.getCachedFront("inspector");
625       if (!inspectorFront) {
626         // initialize the inspector front asyncronously. There is a potential for buggy
627         // behavior here, but we need to change how the buttons get data (have them
628         // consume data from reducers rather than writing our own version) in order to
629         // fix this properly.
630         return false;
631       }
633       return highlighters.every(name =>
634         inspectorFront.getKnownHighlighter(name)?.isShown()
635       );
636     },
637     isToggle: true,
638   };
642  * Lookup l10n string from a string bundle.
644  * @param {string} name
645  *        The key to lookup.
646  * @param {...string} args
647  *        Optional format argument.
648  * @returns A localized version of the given key.
649  */
650 function l10n(name, ...args) {
651   try {
652     return args ? L10N.getFormatStr(name, ...args) : L10N.getStr(name);
653   } catch (ex) {
654     console.log("Error reading '" + name + "'");
655     throw new Error("l10n error with " + name);
656   }
659 function commandkey(name) {
660   try {
661     return CommandKeys.formatValueSync(name);
662   } catch (ex) {
663     console.log("Error reading '" + name + "'");
664     throw new Error("l10n error with " + name);
665   }
668 function functionkey(shortkey) {
669   return shortkey.split("_")[1];