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/. */
7 const osString = Services.appinfo.OS;
14 require("resource://devtools/client/framework/toolbox-options.js")
20 () => require("resource://devtools/client/inspector/panel.js").InspectorPanel
26 require("resource://devtools/client/webconsole/panel.js").WebConsolePanel
31 () => require("resource://devtools/client/debugger/panel.js").DebuggerPanel
37 require("resource://devtools/client/styleeditor/panel.js").StyleEditorPanel
42 () => require("resource://devtools/client/memory/panel.js").MemoryPanel
46 "NewPerformancePanel",
48 require("resource://devtools/client/performance-new/panel/panel.js")
55 require("resource://devtools/client/netmonitor/panel.js").NetMonitorPanel
60 () => require("resource://devtools/client/storage/panel.js").StoragePanel
65 () => require("resource://devtools/client/dom/panel.js").DomPanel
71 require("resource://devtools/client/accessibility/panel.js")
78 require("resource://devtools/client/application/panel.js").ApplicationPanel
82 loader.lazyRequireGetter(
84 "ResponsiveUIManager",
85 "resource://devtools/client/responsive/manager.js"
89 ChromeUtils.defineESModuleGetters(lazy, {
90 AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
92 loader.lazyRequireGetter(
94 "DevToolsExperimentalPrefs",
95 "resource://devtools/client/devtools-experimental-prefs.js"
97 loader.lazyRequireGetter(
99 "captureAndSaveScreenshot",
100 "resource://devtools/client/shared/screenshot.js",
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"],
114 exports.Tools = Tools;
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"),
125 panelLabel: l10n("options.panelLabel"),
126 tooltip: l10n("optionsButton.tooltip"),
133 build(iframeWindow, toolbox, commands) {
134 return new OptionsPanel(iframeWindow, toolbox, commands);
140 accesskey: l10n("inspector.accesskey"),
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"),
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);
154 const ctrlShiftC = "Ctrl+Shift+" + key;
155 return l10n("inspector.tooltip2", ctrlShiftC);
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) {
165 Services.prefs.getBoolPref("devtools.command-button-pick.enabled", false)
167 toolbox.nodePicker.togglePicker();
171 isToolSupported(toolbox) {
172 return toolbox.target.hasActor("inspector");
175 build(iframeWindow, toolbox, commands) {
176 return new InspectorPanel(iframeWindow, toolbox, commands);
181 accesskey: l10n("webConsoleCmd.accesskey"),
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"),
190 "ToolboxWebconsole.tooltip2",
191 (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
192 commandkey("devtools-commandkey-webconsole")
197 preventClosingOnKey: true,
198 onkey(panel, toolbox) {
199 if (toolbox.splitConsole) {
200 return toolbox.focusConsoleInput();
210 build(iframeWindow, toolbox, commands) {
211 return new WebConsolePanel(iframeWindow, toolbox, commands);
217 accesskey: l10n("debuggerMenu.accesskey"),
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"),
225 "ToolboxDebugger.tooltip4",
226 (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
227 commandkey("devtools-commandkey-jsdebugger")
234 build(iframeWindow, toolbox, commands) {
235 return new DebuggerPanel(iframeWindow, toolbox, commands);
239 Tools.styleEditor = {
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"),
250 "ToolboxStyleEditor.tooltip3",
251 "Shift+" + functionkey(commandkey("devtools-commandkey-styleeditor"))
255 isToolSupported(toolbox) {
256 return toolbox.target.hasActor("styleSheets");
259 build(iframeWindow, toolbox, commands) {
260 return new StyleEditorPanel(iframeWindow, toolbox, commands);
264 Tools.performance = {
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"),
274 "performance.tooltip",
275 "Shift+" + functionkey(commandkey("devtools-commandkey-performance"))
278 accesskey: l10n("performance.accesskey"),
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.
288 toolbox.commands.descriptorFront.isLocalTab || toolbox.isBrowserToolbox
291 build(frame, toolbox, commands) {
292 return new NewPerformancePanel(frame, toolbox, commands);
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;
309 !descriptorFront.isWebExtensionDescriptor &&
310 !descriptorFront.isWorkerDescriptor
314 build(frame, toolbox, commands) {
315 return new MemoryPanel(frame, toolbox, commands);
321 accesskey: l10n("netmonitor.accesskey"),
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"),
330 "netmonitor.tooltip2",
331 (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
332 commandkey("devtools-commandkey-netmonitor")
337 isToolSupported(toolbox) {
339 toolbox.target.getTrait("networkMonitor") &&
340 !toolbox.target.isWorkerTarget
344 build(iframeWindow, toolbox, commands) {
345 return new NetMonitorPanel(iframeWindow, toolbox, commands);
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"),
362 "Shift+" + functionkey(commandkey("devtools-commandkey-storage"))
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.
372 descriptorFront.isTabDescriptor ||
373 descriptorFront.isParentProcessDescriptor ||
374 descriptorFront.isWebExtensionDescriptor
378 build(iframeWindow, toolbox, commands) {
379 return new StoragePanel(iframeWindow, toolbox, commands);
385 accesskey: l10n("dom.accesskey"),
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"),
395 (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
396 commandkey("devtools-commandkey-dom")
405 build(iframeWindow, toolbox, commands) {
406 return new DomPanel(iframeWindow, toolbox, commands);
410 Tools.accessibility = {
412 accesskey: l10n("accessibility.accesskey"),
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"),
422 "accessibility.tooltip3",
424 functionkey(commandkey("devtools-commandkey-accessibility-f12"))
429 isToolSupported(toolbox) {
430 return toolbox.target.hasActor("accessibility");
433 build(iframeWindow, toolbox, commands) {
434 return new AccessibilityPanel(iframeWindow, toolbox, commands);
438 Tools.application = {
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"),
449 isToolSupported(toolbox) {
450 return toolbox.target.hasActor("manifest");
453 build(iframeWindow, toolbox, commands) {
454 return new ApplicationPanel(iframeWindow, toolbox, commands);
473 exports.defaultTools = defaultTools;
477 label: l10n("options.darkTheme.label2"),
479 stylesheets: ["chrome://devtools/skin/dark-theme.css"],
480 classList: ["theme-dark"],
485 label: l10n("options.lightTheme.label2"),
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 = [
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(),
505 id: "command-button-responsive",
507 "toolbox.buttons.responsive",
508 osString == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"
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, {
519 const { localTab } = toolbox.commands.descriptorFront;
523 return ResponsiveUIManager.isActiveForTab(localTab);
526 setup(toolbox, onChange) {
527 ResponsiveUIManager.on("on", onChange);
528 ResponsiveUIManager.on("off", onChange);
530 teardown(toolbox, onChange) {
531 ResponsiveUIManager.off("on", onChange);
532 ResponsiveUIManager.off("off", onChange);
536 id: "command-button-screenshot",
537 description: l10n("toolbox.buttons.screenshot"),
538 isToolSupported: toolbox => {
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")
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"
552 // When screenshot to clipboard is enabled disabling saving to file
555 file: !clipboardEnabled,
556 clipboard: clipboardEnabled,
559 const messages = await captureAndSaveScreenshot(
564 const notificationBox = toolbox.getNotificationBox();
565 const priorityMap = {
566 error: notificationBox.PRIORITY_CRITICAL_HIGH,
567 warn: notificationBox.PRIORITY_WARNING_HIGH,
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.
576 !toolbox.isBrowserToolbox &&
582 notificationBox.appendNotification(
586 priorityMap[level] || notificationBox.PRIORITY_INFO_MEDIUM
591 createHighlightButton(
592 ["RulersHighlighter", "ViewportSizeHighlighter"],
595 createHighlightButton(["MeasuringToolHighlighter"], "measure"),
598 function createHighlightButton(highlighters, id) {
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");
608 highlighters.map(async name => {
609 const highlighter = await inspectorFront.getOrCreateHighlighterByType(
613 if (highlighter.isShown()) {
614 await highlighter.hide();
616 await highlighter.show();
622 // if the inspector doesn't exist, then the highlighter has not yet been connected
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.
633 return highlighters.every(name =>
634 inspectorFront.getKnownHighlighter(name)?.isShown()
642 * Lookup l10n string from a string bundle.
644 * @param {string} name
646 * @param {...string} args
647 * Optional format argument.
648 * @returns A localized version of the given key.
650 function l10n(name, ...args) {
652 return args ? L10N.getFormatStr(name, ...args) : L10N.getStr(name);
654 console.log("Error reading '" + name + "'");
655 throw new Error("l10n error with " + name);
659 function commandkey(name) {
661 return CommandKeys.formatValueSync(name);
663 console.log("Error reading '" + name + "'");
664 throw new Error("l10n error with " + name);
668 function functionkey(shortkey) {
669 return shortkey.split("_")[1];