From 66797d7700da4390d62b9c7e93375e68691fc41b Mon Sep 17 00:00:00 2001 From: Norisz Fay Date: Mon, 28 Mar 2022 13:11:58 +0300 Subject: [PATCH] Backed out 5 changesets (bug 1609100) for causing mochitest failures on browser_check_identity_state.js CLOSED TREE Backed out changeset 521cbbae0914 (bug 1609100) Backed out changeset e22daee724f0 (bug 1609100) Backed out changeset e5c4afe5dd66 (bug 1609100) Backed out changeset e6ae2c01908e (bug 1609100) Backed out changeset 3e59351660ab (bug 1609100) --- .../enterprisepolicies/Policies.jsm | 1 + .../browser/browser_policy_block_about.js | 7 +- .../browser_policy_disable_developer_tools.js | 1 + devtools/.eslintrc.js | 1 + .../client/aboutdebugging/aboutdebugging.js | 6 + devtools/startup/DevToolsShim.jsm | 11 +- devtools/startup/DevToolsStartup.jsm | 170 +++++++++++- .../AboutDevToolsRegistration.jsm | 40 +++ .../startup/aboutdevtools/aboutdevtools.css | 184 +++++++++++++ .../startup/aboutdevtools/aboutdevtools.js | 250 +++++++++++++++++ .../startup/aboutdevtools/aboutdevtools.xhtml | 109 ++++++++ .../startup/aboutdevtools/components.conf | 14 + .../aboutdevtools/images/dev-edition-logo.svg | 251 ++++++++++++++++++ .../aboutdevtools/images/external-link.svg | 7 + .../aboutdevtools/images/feature-console.svg | 9 + .../aboutdevtools/images/feature-debugger.svg | 9 + .../images/feature-inspector.svg | 9 + .../aboutdevtools/images/feature-memory.svg | 9 + .../aboutdevtools/images/feature-network.svg | 10 + .../images/feature-performance.svg | 14 + .../images/feature-responsive.svg | 14 + .../aboutdevtools/images/feature-storage.svg | 9 + .../images/feature-visualediting.svg | 9 + .../startup/aboutdevtools/images/otter.svg | 29 ++ devtools/startup/aboutdevtools/moz.build | 15 ++ devtools/startup/aboutdevtools/subscribe.css | 94 +++++++ devtools/startup/aboutdevtools/subscribe.js | 158 +++++++++++ .../startup/aboutdevtools/test/.eslintrc.js | 7 + .../startup/aboutdevtools/test/browser.ini | 11 + .../test/browser_aboutdevtools_closes_page.js | 25 ++ .../browser_aboutdevtools_enables_devtools.js | 39 +++ .../browser_aboutdevtools_focus_owner_tab.js | 91 +++++++ .../browser_aboutdevtools_reuse_existing.js | 46 ++++ devtools/startup/aboutdevtools/test/head.js | 135 ++++++++++ devtools/startup/jar.mn | 19 ++ .../startup/locales/en-US/aboutDevTools.ftl | 57 ++++ .../startup/locales/en-US/startup.properties | 8 + devtools/startup/locales/jar.mn | 5 + devtools/startup/moz.build | 1 + .../browser/browser_shim_disable_devtools.js | 7 + .../tests/xpcshell/test_devtools_shim.js | 33 ++- dom/console/Console.cpp | 18 ++ dom/console/Console.h | 2 + dom/console/tests/mochitest.ini | 1 + dom/console/tests/test_devtools_pref.html | 65 +++++ dom/security/DOMSecurityMonitor.cpp | 2 + modules/libpref/init/StaticPrefList.yaml | 8 + modules/libpref/init/all.js | 2 +- 48 files changed, 1993 insertions(+), 29 deletions(-) create mode 100644 devtools/startup/aboutdevtools/AboutDevToolsRegistration.jsm create mode 100644 devtools/startup/aboutdevtools/aboutdevtools.css create mode 100644 devtools/startup/aboutdevtools/aboutdevtools.js create mode 100644 devtools/startup/aboutdevtools/aboutdevtools.xhtml create mode 100644 devtools/startup/aboutdevtools/components.conf create mode 100644 devtools/startup/aboutdevtools/images/dev-edition-logo.svg create mode 100644 devtools/startup/aboutdevtools/images/external-link.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-console.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-debugger.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-inspector.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-memory.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-network.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-performance.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-responsive.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-storage.svg create mode 100644 devtools/startup/aboutdevtools/images/feature-visualediting.svg create mode 100644 devtools/startup/aboutdevtools/images/otter.svg create mode 100644 devtools/startup/aboutdevtools/moz.build create mode 100644 devtools/startup/aboutdevtools/subscribe.css create mode 100644 devtools/startup/aboutdevtools/subscribe.js create mode 100644 devtools/startup/aboutdevtools/test/.eslintrc.js create mode 100644 devtools/startup/aboutdevtools/test/browser.ini create mode 100644 devtools/startup/aboutdevtools/test/browser_aboutdevtools_closes_page.js create mode 100644 devtools/startup/aboutdevtools/test/browser_aboutdevtools_enables_devtools.js create mode 100644 devtools/startup/aboutdevtools/test/browser_aboutdevtools_focus_owner_tab.js create mode 100644 devtools/startup/aboutdevtools/test/browser_aboutdevtools_reuse_existing.js create mode 100644 devtools/startup/aboutdevtools/test/head.js create mode 100644 devtools/startup/locales/en-US/aboutDevTools.ftl create mode 100644 devtools/startup/locales/en-US/startup.properties create mode 100644 dom/console/tests/test_devtools_pref.html diff --git a/browser/components/enterprisepolicies/Policies.jsm b/browser/components/enterprisepolicies/Policies.jsm index 3a2b6159c130..ed82f01b2e1f 100644 --- a/browser/components/enterprisepolicies/Policies.jsm +++ b/browser/components/enterprisepolicies/Policies.jsm @@ -584,6 +584,7 @@ var Policies = { setAndLockPref("devtools.chrome.enabled", false); manager.disallowFeature("devtools"); + blockAboutPage(manager, "about:devtools"); blockAboutPage(manager, "about:debugging"); blockAboutPage(manager, "about:devtools-toolbox"); blockAboutPage(manager, "about:profiling"); diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js index 9c7594db609c..74a5e8db10cd 100644 --- a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js +++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about.js @@ -33,7 +33,12 @@ const policiesToTest = [ policies: { DisableDeveloperTools: true, }, - urls: ["about:debugging", "about:devtools-toolbox", "about:profiling"], + urls: [ + "about:devtools", + "about:debugging", + "about:devtools-toolbox", + "about:profiling", + ], }, { policies: { diff --git a/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js b/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js index 515627882147..7850deab4f9f 100644 --- a/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js +++ b/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js @@ -27,6 +27,7 @@ add_task(async function test_updates_post_policy() { "devtools dedicated disabled pref can not be updated" ); + await expectErrorPage("about:devtools"); await expectErrorPage("about:devtools-toolbox"); await expectErrorPage("about:debugging"); diff --git a/devtools/.eslintrc.js b/devtools/.eslintrc.js index d36ccc88d0f6..47e23952f239 100644 --- a/devtools/.eslintrc.js +++ b/devtools/.eslintrc.js @@ -121,6 +121,7 @@ module.exports = { "shared/loader/browser-loader.js", "shared/loader/worker-loader.js", "startup/aboutdebugging-registration.js", + "startup/aboutdevtools/aboutdevtools-registration.js", "startup/aboutdevtoolstoolbox-registration.js", "startup/devtools-startup.js", ], diff --git a/devtools/client/aboutdebugging/aboutdebugging.js b/devtools/client/aboutdebugging/aboutdebugging.js index 2be74f2ae2da..4c9385bc3fc8 100644 --- a/devtools/client/aboutdebugging/aboutdebugging.js +++ b/devtools/client/aboutdebugging/aboutdebugging.js @@ -68,6 +68,12 @@ const App = createFactory( const AboutDebugging = { async init() { + if (!Services.prefs.getBoolPref("devtools.enabled", true)) { + // If DevTools are disabled, navigate to about:devtools. + window.location = "about:devtools?reason=AboutDebugging"; + return; + } + const direction = Services.locale.isAppLocaleRTL ? "rtl" : "ltr"; document.documentElement.setAttribute("dir", direction); diff --git a/devtools/startup/DevToolsShim.jsm b/devtools/startup/DevToolsShim.jsm index 4f0f296b4a1d..ecb5b47264fc 100644 --- a/devtools/startup/DevToolsShim.jsm +++ b/devtools/startup/DevToolsShim.jsm @@ -27,6 +27,7 @@ XPCOMUtils.defineLazyGetter(this, "Telemetry", function() { return Telemetry; }); +const DEVTOOLS_ENABLED_PREF = "devtools.enabled"; const DEVTOOLS_POLICY_DISABLED_PREF = "devtools.policy.disabled"; const EXPORTED_SYMBOLS = ["DevToolsShim"]; @@ -43,7 +44,7 @@ function removeItem(array, callback) { * that work whether Devtools are enabled or not. * * It can be used to start listening to devtools events before DevTools are ready. As soon - * as DevTools are ready, the DevToolsShim will forward all the requests received until + * as DevTools are enabled, the DevToolsShim will forward all the requests received until * then to the real DevTools instance. */ const DevToolsShim = { @@ -59,11 +60,13 @@ const DevToolsShim = { }, /** - * Returns true if DevTools are enabled. This now only depends on the policy. - * TODO: Merge isEnabled and isDisabledByPolicy. + * Returns true if DevTools are enabled for the current profile. If devtools are not + * enabled, initializing DevTools will open the onboarding page. Some entry points + * should no-op in this case. */ isEnabled: function() { - return !this.isDisabledByPolicy(); + const enabled = Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF); + return enabled && !this.isDisabledByPolicy(); }, /** diff --git a/devtools/startup/DevToolsStartup.jsm b/devtools/startup/DevToolsStartup.jsm index c38afa153340..97144a28c39b 100644 --- a/devtools/startup/DevToolsStartup.jsm +++ b/devtools/startup/DevToolsStartup.jsm @@ -27,6 +27,7 @@ const kDebuggerPrefs = [ "devtools.chrome.enabled", ]; +const DEVTOOLS_ENABLED_PREF = "devtools.enabled"; const DEVTOOLS_F12_DISABLED_PREF = "devtools.experiment.f12.shortcut_disabled"; const DEVTOOLS_POLICY_DISABLED_PREF = "devtools.policy.disabled"; @@ -88,6 +89,11 @@ XPCOMUtils.defineLazyGetter(this, "Telemetry", function() { return Telemetry; }); +XPCOMUtils.defineLazyGetter(this, "StartupBundle", function() { + const url = "chrome://devtools-startup/locale/startup.properties"; + return Services.strings.createBundle(url); +}); + XPCOMUtils.defineLazyGetter(this, "KeyShortcutsBundle", function() { return new Localization(["devtools/startup/key-shortcuts.ftl"], true); }); @@ -312,6 +318,7 @@ XPCOMUtils.defineLazyGetter(this, "ProfilerPopupBackground", function() { }); function DevToolsStartup() { + this.onEnabledPrefChanged = this.onEnabledPrefChanged.bind(this); this.onWindowReady = this.onWindowReady.bind(this); this.addDevToolsItemsToSubview = this.addDevToolsItemsToSubview.bind(this); this.onMoreToolsViewShowing = this.onMoreToolsViewShowing.bind(this); @@ -362,6 +369,10 @@ DevToolsStartup.prototype = { const isInitialLaunch = cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH; if (isInitialLaunch) { + // Enable devtools for all users on startup (onboarding experiment from Bug 1408969 + // is over). + Services.prefs.setBoolPref(DEVTOOLS_ENABLED_PREF, true); + // The F12 shortcut might be disabled to avoid accidental usage. // Users who are already considered as devtools users should not be // impacted. @@ -381,6 +392,12 @@ DevToolsStartup.prototype = { "browser-delayed-startup-finished" ); + // Update menu items when devtools.enabled changes. + Services.prefs.addObserver( + DEVTOOLS_ENABLED_PREF, + this.onEnabledPrefChanged + ); + // Add DevTools menu items to the "More Tools" view. Services.obs.addObserver( this.onMoreToolsViewShowing, @@ -508,6 +525,9 @@ DevToolsStartup.prototype = { if (!this.initialized) { this.hookBrowserToolsMenu(window); } + + this.createDevToolsEnableMenuItem(window); + this.updateDevToolsMenuItems(window); }, /** @@ -570,9 +590,11 @@ DevToolsStartup.prototype = { }, addDevToolsItemsToSubview(subview) { - // Initialize DevTools to create all menuitems in the system menu before - // trying to copy them. - this.initDevTools("HamburgerMenu"); + if (Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) { + // If DevTools are enabled, initialize DevTools to create all menuitems in the + // system menu before trying to copy them. + this.initDevTools("HamburgerMenu"); + } // Populate the subview with whatever menuitems are in the developer // menu. We skip menu elements, because the menu panel has no way @@ -674,12 +696,64 @@ DevToolsStartup.prototype = { hookBrowserToolsMenu(window) { const menu = window.document.getElementById("browserToolsMenu"); const onPopupShowing = () => { + if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) { + return; + } menu.removeEventListener("popupshowing", onPopupShowing); this.initDevTools("SystemMenu"); }; menu.addEventListener("popupshowing", onPopupShowing); }, + /** + * Create a new menu item to enable DevTools and insert it DevTools's submenu in the + * System Menu. + */ + createDevToolsEnableMenuItem(window) { + const { document } = window; + + // Create the menu item. + const item = document.createXULElement("menuitem"); + item.id = "enableDeveloperTools"; + item.setAttribute( + "label", + StartupBundle.GetStringFromName("enableDevTools.label") + ); + item.setAttribute( + "accesskey", + StartupBundle.GetStringFromName("enableDevTools.accesskey") + ); + + // The menu item should open the install page for DevTools. + item.addEventListener("command", () => { + this.openInstallPage("SystemMenu"); + }); + + // Insert the menu item in the DevTools submenu. + const systemMenuItem = document.getElementById("menuWebDeveloperPopup"); + systemMenuItem.appendChild(item); + }, + + /** + * Update the visibility the menu item to enable DevTools. + */ + updateDevToolsMenuItems(window) { + const item = window.document.getElementById("enableDeveloperTools"); + item.hidden = Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF); + }, + + /** + * Loop on all windows and update the hidden attribute of the "enable DevTools" menu + * item. + */ + onEnabledPrefChanged() { + for (const window of Services.wm.getEnumerator("navigator:browser")) { + if (window.gBrowserInit && window.gBrowserInit.delayedStartupFinished) { + this.updateDevToolsMenuItems(window); + } + } + }, + /** * Check if the user is a DevTools user by looking at our selfxss pref. * This preference is incremented everytime the console is used (up to 5). @@ -790,16 +864,20 @@ DevToolsStartup.prototype = { return; } } - - // Record the timing at which this event started in order to compute later in - // gDevTools.showToolbox, the complete time it takes to open the toolbox. - // i.e. especially take `initDevTools` into account. - const startTime = Cu.now(); - const require = this.initDevTools("KeyShortcut", key); - const { - gDevToolsBrowser, - } = require("devtools/client/framework/devtools-browser"); - await gDevToolsBrowser.onKeyShortcut(window, key, startTime); + if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) { + const id = key.toolId || key.id; + this.openInstallPage("KeyShortcut", id); + } else { + // Record the timing at which this event started in order to compute later in + // gDevTools.showToolbox, the complete time it takes to open the toolbox. + // i.e. especially take `initDevTools` into account. + const startTime = Cu.now(); + const require = this.initDevTools("KeyShortcut", key); + const { + gDevToolsBrowser, + } = require("devtools/client/framework/devtools-browser"); + await gDevToolsBrowser.onKeyShortcut(window, key, startTime); + } } catch (e) { console.error(`Exception while trigerring key ${key}: ${e}\n${e.stack}`); } @@ -836,6 +914,12 @@ DevToolsStartup.prototype = { }, initDevTools: function(reason, key = "") { + // If an entry point is fired and tools are not enabled open the installation page + if (!Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) { + this.openInstallPage(reason); + return null; + } + // In the case of the --jsconsole and --jsdebugger command line parameters // there is no browser window yet so we don't send any telemetry yet. if (reason !== "CommandLine") { @@ -852,6 +936,66 @@ DevToolsStartup.prototype = { return require; }, + /** + * Open about:devtools to start the onboarding flow. + * + * @param {String} reason + * One of "KeyShortcut", "SystemMenu", "HamburgerMenu", "ContextMenu", + * "CommandLine". + * @param {String} keyId + * Optional. If the onboarding flow was triggered by a keyboard shortcut, pass + * the shortcut key id (or toolId) to about:devtools. + */ + openInstallPage: function(reason, keyId) { + // If DevTools are completely disabled, bail out here as this might be called directly + // from other files. + if (this.isDisabledByPolicy()) { + return; + } + + const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser"); + + // Focus about:devtools tab if there is already one opened in the current window. + for (const tab of gBrowser.tabs) { + const browser = tab.linkedBrowser; + // browser.documentURI might be undefined if the browser tab is still loading. + const location = browser.documentURI ? browser.documentURI.spec : ""; + if ( + location.startsWith("about:devtools") && + !location.startsWith("about:devtools-toolbox") + ) { + // Focus the existing about:devtools tab and bail out. + gBrowser.selectedTab = tab; + return; + } + } + + let url = "about:devtools"; + + const params = []; + if (reason) { + params.push("reason=" + encodeURIComponent(reason)); + } + + const selectedBrowser = gBrowser.selectedBrowser; + if (selectedBrowser) { + params.push("tabid=" + selectedBrowser.outerWindowID); + } + + if (keyId) { + params.push("keyid=" + keyId); + } + + if (params.length > 0) { + url += "?" + params.join("&"); + } + + // Set relatedToCurrent: true to open the tab next to the current one. + gBrowser.selectedTab = gBrowser.addTrustedTab(url, { + relatedToCurrent: true, + }); + }, + handleConsoleFlag: function(cmdLine) { const window = Services.wm.getMostRecentWindow("devtools:webconsole"); if (!window) { diff --git a/devtools/startup/aboutdevtools/AboutDevToolsRegistration.jsm b/devtools/startup/aboutdevtools/AboutDevToolsRegistration.jsm new file mode 100644 index 000000000000..09065698d33d --- /dev/null +++ b/devtools/startup/aboutdevtools/AboutDevToolsRegistration.jsm @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Register the about:devtools URL, that is opened whenever a user attempts to open +// DevTools for the first time. +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { nsIAboutModule } = Ci; + +function AboutDevtools() {} + +AboutDevtools.prototype = { + uri: Services.io.newURI( + "chrome://devtools-startup/content/aboutdevtools/aboutdevtools.xhtml" + ), + classDescription: "about:devtools", + classID: Components.ID("3a16d383-92bd-4c24-ac10-0e2bd66883ab"), + contractID: "@mozilla.org/network/protocol/about;1?what=devtools", + + QueryInterface: ChromeUtils.generateQI([nsIAboutModule]), + + newChannel: function(uri, loadInfo) { + const chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, loadInfo); + chan.owner = Services.scriptSecurityManager.getSystemPrincipal(); + return chan; + }, + + getURIFlags: function(uri) { + return nsIAboutModule.ALLOW_SCRIPT | nsIAboutModule.IS_SECURE_CHROME_UI; + }, + + getChromeURI: function(_uri) { + return this.uri; + }, +}; + +var EXPORTED_SYMBOLS = ["AboutDevtools"]; diff --git a/devtools/startup/aboutdevtools/aboutdevtools.css b/devtools/startup/aboutdevtools/aboutdevtools.css new file mode 100644 index 000000000000..1709f7a82899 --- /dev/null +++ b/devtools/startup/aboutdevtools/aboutdevtools.css @@ -0,0 +1,184 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +:root { + --white: #ffffff; + + /* Shared variables */ + --line-height: 1.5em; +} + +html, body { + min-width: 600px; +} + +body { + margin-top: 17vh; +} + +p { + line-height: var(--line-height); +} + +.box { + max-width: 850px; + display: flex; + flex-shrink: 0; + padding: 0 0 50px 0; + /* Compensate for the optional scrollbar. */ + margin-right: calc(100% - 100vw); +} + +.wrapper:not([hidden]) { + display: flex; + flex-direction: column; + align-items: center; +} + +.left-pane { + width: 300px; + height: 300px; + margin-inline-end: 20px; + background-image: url(images/otter.svg); + background-size: 100%; + background-position: 50%; + background-repeat: no-repeat; + flex-shrink: 0; +} + +.features { + max-width: 980px; + border-top: 1px solid var(--in-content-border-color); +} + +.features-list { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 40px 20px; + margin: 60px 20px; + padding: 0; +} + +.feature-icon { + width: 60%; + max-width: 180px; + margin-bottom: 10px; +} + +.feature { + list-style: none; + text-align: center; + margin: 10px 0; +} + +.feature-name { + display: block; + margin: 10px 0; + font-size: 28px; + font-weight: 300; +} + +.feature-name span { + display: block; +} + +.feature-desc { + margin: 1em 20px; +} + +.feature-link { + display: block; + color: inherit; +} + +.feature-more-link { + display: block; + margin-top: 10px; +} + +.external::after { + content: ""; + + display: inline-block; + height: 16px; + width: 16px; + + margin: -.3rem .15rem 0 0.25rem; + vertical-align: middle; + + background-image: url(images/external-link.svg); + background-repeat: no-repeat; + background-size: 16px 16px; + + -moz-context-properties: fill; + fill: currentColor; +} + +.title { + font-weight: 300; + font-size: 32px; + margin-top: 16px; + line-height: 44px; +} + +.buttons-container { + display: flex; + margin-top: 5px; +} + +.buttons-container button:not(:last-child) { + margin-right: 10px; +} + +button { + margin: 20px 0 0 0; + padding: 10px 20px; + + font-size: 15px; + line-height: 21px; + cursor: pointer; +} + +/* Remove light gray outline when clicking on the button */ +button::-moz-focus-inner { + border: 0; +} + +footer { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 300px; + flex-grow: 1; + padding-bottom: 15px; + color: var(--white); + background: linear-gradient(0, var(--blue-60), var(--blue-80)); +} + +.dev-edition-logo { + flex-shrink: 0; + width: 165px; + margin: 20px 50px 0 0; +} + +.footer-message { + max-width: 460px; +} + +.footer-message-title { + color: var(--white); +} + +.footer-link { + display: inline; + margin-top: 10px; +} + +.footer-link, +.footer-link:hover, +.footer-link:visited, +.footer-link:hover:active { + color: var(--white); +} diff --git a/devtools/startup/aboutdevtools/aboutdevtools.js b/devtools/startup/aboutdevtools/aboutdevtools.js new file mode 100644 index 000000000000..e6cc7ed9a25e --- /dev/null +++ b/devtools/startup/aboutdevtools/aboutdevtools.js @@ -0,0 +1,250 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const DEVTOOLS_ENABLED_PREF = "devtools.enabled"; + +const MESSAGES = { + AboutDebugging: "about-debugging-message", + ContextMenu: "inspect-element-message", + HamburgerMenu: "menu-message", + KeyShortcut: "key-shortcut-message", + SystemMenu: "menu-message", +}; + +// Google analytics parameters that should be added to all outgoing links. +const GA_PARAMETERS = [ + ["utm_source", "devtools"], + ["utm_medium", "onboarding"], +]; + +const KEY_SHORTCUTS_STRINGS = "devtools/startup/key-shortcuts.ftl"; +const keyShortcutsBundle = new Localization([KEY_SHORTCUTS_STRINGS], true); + +// URL constructor doesn't support about: scheme, +// we have to use http in order to have working searchParams. +const url = new URL(window.location.href.replace("about:", "http://")); +const reason = url.searchParams.get("reason"); +const tabid = parseInt(url.searchParams.get("tabid"), 10); + +function getToolboxShortcut() { + const modifier = Services.appinfo.OS == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+"; + return ( + modifier + + keyShortcutsBundle.formatValueSync("devtools-commandkey-toggle-toolbox") + ); +} + +function onInstallButtonClick() { + Services.prefs.setBoolPref("devtools.enabled", true); +} + +function onCloseButtonClick() { + window.close(); +} + +function updatePage() { + const isEnabled = Services.prefs.getBoolPref("devtools.enabled"); + document.getElementById("install-page").hidden = isEnabled; + document.getElementById("welcome-page").hidden = !isEnabled; +} + +/** + * Array of descriptors for features displayed on about:devtools. + * Each feature should contain: + * - icon: the name of the image to use + * - title: the key of the localized title (from aboutDevTools.ftl) + * - desc: the key of the localized description (from aboutDevTools.ftl) + * - link: the MDN documentation link + */ +const features = [ + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-inspector.svg", + title: "features-inspector-title", + desc: "features-inspector-desc", + link: + "https://firefox-source-docs.mozilla.org/devtools-user/page_inspector/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-console.svg", + title: "features-console-title", + desc: "features-console-desc", + link: "https://firefox-source-docs.mozilla.org/devtools-user/web_console/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-debugger.svg", + title: "features-debugger-title", + desc: "features-debugger-desc", + link: "https://firefox-source-docs.mozilla.org/devtools-user/debugger/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-network.svg", + title: "features-network-title", + desc: "features-network-desc", + link: + "https://firefox-source-docs.mozilla.org/devtools-user/network_monitor/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-storage.svg", + title: "features-storage-title", + desc: "features-storage-desc", + link: + "https://firefox-source-docs.mozilla.org/devtools-user/storage_inspector/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-responsive.svg", + title: "features-responsive-title", + desc: "features-responsive-desc", + link: + "https://firefox-source-docs.mozilla.org/devtools-user/responsive_design_mode/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-visualediting.svg", + title: "features-visual-editing-title", + desc: "features-visual-editing-desc", + link: "https://firefox-source-docs.mozilla.org/devtools-user/style_editor/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-performance.svg", + title: "features-performance-title", + desc: "features-performance-desc", + link: "https://firefox-source-docs.mozilla.org/devtools-user/performance/", + }, + { + icon: + "chrome://devtools-startup/content/aboutdevtools/images/feature-memory.svg", + title: "features-memory-title", + desc: "features-memory-desc", + link: "https://firefox-source-docs.mozilla.org/devtools-user/memory/", + }, +]; + +/** + * Helper to create a DOM element to represent a DevTools feature. + */ +function createFeatureEl(feature) { + const li = document.createElement("li"); + li.classList.add("feature"); + + const { icon, link, title, desc } = feature; + // eslint-disable-next-line no-unsanitized/property + li.innerHTML = ` +

+ + + + +

+

+ +

`; + + return li; +} + +window.addEventListener( + "load", + function() { + const inspectorShortcut = getToolboxShortcut(); + const welcomeMessage = document.getElementById("welcome-message"); + + // Set the welcome message content with the correct keyboard shortcut for the current + // platform. + document.l10n.setAttributes(welcomeMessage, "welcome-message", { + shortcut: inspectorShortcut, + }); + + // Set the appropriate title message. + if (reason == "ContextMenu") { + document.getElementById("inspect-title").removeAttribute("hidden"); + } else { + document.getElementById("common-title").removeAttribute("hidden"); + } + + // Display the message specific to the reason + const id = MESSAGES[reason]; + if (id) { + const message = document.getElementById(id); + message.removeAttribute("hidden"); + } + + // Attach event listeners + document + .getElementById("install") + .addEventListener("click", onInstallButtonClick); + document + .getElementById("close") + .addEventListener("click", onCloseButtonClick); + Services.prefs.addObserver(DEVTOOLS_ENABLED_PREF, updatePage); + + const featuresContainer = document.querySelector(".features-list"); + for (const feature of features) { + featuresContainer.appendChild(createFeatureEl(feature)); + } + + // Add Google Analytics parameters to all the external links. + const externalLinks = document.querySelectorAll("a[href*='mozilla.org']"); + for (const link of externalLinks) { + const linkUrl = new URL(link.getAttribute("href")); + GA_PARAMETERS.forEach(([key, value]) => + linkUrl.searchParams.set(key, value) + ); + link.setAttribute("href", linkUrl.href); + } + + // Update the current page based on the current value of DEVTOOLS_ENABLED_PREF. + updatePage(); + }, + { once: true } +); + +window.addEventListener( + "beforeunload", + function() { + // Focus the tab that triggered the DevTools onboarding. + if (document.visibilityState != "visible") { + // Only try to focus the correct tab if the current tab is the about:devtools page. + return; + } + + // Retrieve the original tab if it is still available. + const browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + const { gBrowser } = browserWindow; + const originalBrowser = gBrowser.getBrowserForOuterWindowID(tabid); + const originalTab = gBrowser.getTabForBrowser(originalBrowser); + + if (originalTab) { + // If the original tab was found, select it. + gBrowser.selectedTab = originalTab; + } + }, + { once: true } +); + +window.addEventListener( + "unload", + function() { + document + .getElementById("install") + .removeEventListener("click", onInstallButtonClick); + document + .getElementById("close") + .removeEventListener("click", onCloseButtonClick); + Services.prefs.removeObserver(DEVTOOLS_ENABLED_PREF, updatePage); + }, + { once: true } +); diff --git a/devtools/startup/aboutdevtools/aboutdevtools.xhtml b/devtools/startup/aboutdevtools/aboutdevtools.xhtml new file mode 100644 index 000000000000..fa65a0699f81 --- /dev/null +++ b/devtools/startup/aboutdevtools/aboutdevtools.xhtml @@ -0,0 +1,109 @@ + + + %htmlDTD; + %globalDTD; +]> + + + + + + + a + + + + + + + + +