Bug 1446944 - Provide onboarding tooltip for the 3 pane inspector feature. r=jdescottes, flod draft
authorGabriel Luong <gabriel.luong@gmail.com>
Thu, 03 May 2018 13:54:12 -0400
changeset 791195 d713ccd60d80aa92daa2695610bcdcd13586955f
parent 791194 e33c9d2925843d53f27c4cac609b07e79d2a881a
push id108726
push userbmo:gl@mozilla.com
push dateThu, 03 May 2018 17:54:32 +0000
reviewersjdescottes, flod
bugs1446944
milestone61.0a1
Bug 1446944 - Provide onboarding tooltip for the 3 pane inspector feature. r=jdescottes, flod MozReview-Commit-ID: GZ3jhuoCcuZ
devtools/client/inspector/inspector.js
devtools/client/inspector/shared/moz.build
devtools/client/inspector/shared/three-pane-onboarding-tooltip.js
devtools/client/jar.mn
devtools/client/locales/en-US/inspector.properties
devtools/client/preferences/devtools-client.js
devtools/client/shared/test/shared-head.js
devtools/client/themes/images/fox-smiling.svg
devtools/client/themes/tooltips.css
devtools/client/themes/variables.css
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -8,29 +8,29 @@
 
 "use strict";
 
 const Services = require("Services");
 const promise = require("promise");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {executeSoon} = require("devtools/shared/DevToolsUtils");
 const {Toolbox} = require("devtools/client/framework/toolbox");
-const {PrefObserver} = require("devtools/client/shared/prefs");
 const Telemetry = require("devtools/client/shared/telemetry");
 const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
 const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
 const Store = require("devtools/client/inspector/store");
 const InspectorStyleChangeTracker = require("devtools/client/inspector/shared/style-change-tracker");
 
 // Use privileged promise in panel documents to prevent having them to freeze
 // during toolbox destruction. See bug 1402779.
 const Promise = require("Promise");
 
 loader.lazyRequireGetter(this, "initCssProperties", "devtools/shared/fronts/css-properties", true);
 loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true);
+loader.lazyRequireGetter(this, "ThreePaneOnboardingTooltip", "devtools/client/inspector/shared/three-pane-onboarding-tooltip");
 loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
 loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true);
 loader.lazyRequireGetter(this, "MarkupView", "devtools/client/inspector/markup/markup");
 loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
 loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
 loader.lazyRequireGetter(this, "ExtensionSidebar", "devtools/client/inspector/extensions/extension-sidebar");
@@ -50,16 +50,17 @@ const INITIAL_SIDEBAR_SIZE = 350;
 // If the toolbox's width is smaller than the given amount of pixels, the sidebar
 // automatically switches from 'landscape/horizontal' to 'portrait/vertical' mode.
 const PORTRAIT_MODE_WIDTH_THRESHOLD = 700;
 // If the toolbox's width docked to the side is smaller than the given amount of pixels,
 // the sidebar automatically switches from 'landscape/horizontal' to 'portrait/vertical'
 // mode.
 const SIDE_PORTAIT_MODE_WIDTH_THRESHOLD = 1000;
 
+const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip";
 const SHOW_THREE_PANE_TOGGLE_PREF = "devtools.inspector.three-pane-toggle";
 const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
 const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
 
 /**
  * Represents an open instance of the Inspector for a tab.
  * The inspector controls the breadcrumbs, the markup view, and the sidebar
  * (computed view, rule view, font view and animation inspector).
@@ -106,27 +107,27 @@ function Inspector(toolbox) {
 
   this.store = Store();
 
   // Map [panel id => panel instance]
   // Stores all the instances of sidebar panels like rule view, computed view, ...
   this._panels = new Map();
 
   this.highlighters = new HighlightersOverlay(this);
-  this.prefsObserver = new PrefObserver("devtools.");
   this.reflowTracker = new ReflowTracker(this._target);
   this.styleChangeTracker = new InspectorStyleChangeTracker(this);
   this.telemetry = new Telemetry();
 
   // Store the URL of the target page prior to navigation in order to ensure
   // telemetry counts in the Grid Inspector are not double counted on reload.
   this.previousURL = this.target.url;
 
+  this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF);
   this.show3PaneToggle = Services.prefs.getBoolPref(SHOW_THREE_PANE_TOGGLE_PREF);
-  this.is3PaneModeEnabled = Services.prefs.getBoolPref(THREE_PANE_ENABLED_PREF);
+  this.show3PaneTooltip = Services.prefs.getBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF);
 
   this.nodeMenuTriggerInfo = null;
 
   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
@@ -291,16 +292,20 @@ Inspector.prototype = {
       this.selection.setNodeFront(defaultSelection, { reason: "inspector-open" });
       await onAllPanelsUpdated;
       await this.markup.expandNode(this.selection.nodeFront);
     }
 
     // Setup the toolbar only now because it may depend on the document.
     await this.setupToolbar();
 
+    if (this.show3PaneTooltip) {
+      this.threePaneTooltip = new ThreePaneOnboardingTooltip(this.toolbox, this.panelDoc);
+    }
+
     // Log the 3 pane inspector setting on inspector open. The question we want to answer
     // is:
     // "What proportion of users use the 3 pane vs 2 pane inspector on inspector open?"
     this.telemetry.logKeyedScalar(THREE_PANE_ENABLED_SCALAR, this.is3PaneModeEnabled, 1);
 
     this.emit("ready");
     return this;
   },
@@ -1338,16 +1343,19 @@ Inspector.prototype = {
 
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
 
     this.cancelUpdate();
 
+    this.selection.off("new-node-front", this.onNewSelection);
+    this.selection.off("detached-front", this.onDetached);
+    this.sidebar.off("select", this.onSidebarSelect);
     this.target.off("will-navigate", this._onBeforeNavigate);
     this.target.off("thread-paused", this.updateDebuggerPausedWarning);
     this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
     this._toolbox.off("select", this.updateDebuggerPausedWarning);
 
     for (let [, panel] of this._panels) {
       panel.destroy();
     }
@@ -1360,54 +1368,51 @@ Inspector.prototype = {
     if (this.fontinspector) {
       this.fontinspector.destroy();
     }
 
     if (this.animationinspector) {
       this.animationinspector.destroy();
     }
 
-    let cssPropertiesDestroyer = this._cssProperties.front.destroy();
+    if (this.threePaneTooltip) {
+      this.threePaneTooltip.destroy();
+    }
 
-    this.sidebar.off("select", this.onSidebarSelect);
+    let cssPropertiesDestroyer = this._cssProperties.front.destroy();
     let sidebarDestroyer = this.sidebar.destroy();
-
     let ruleViewSideBarDestroyer = this.ruleViewSideBar ?
       this.ruleViewSideBar.destroy() : null;
+    let markupDestroyer = this._destroyMarkup();
+    let highlighterDestroyer = this.highlighters.destroy();
 
     this.teardownSplitter();
+    this.teardownToolbar();
 
-    this.teardownToolbar();
     this.breadcrumbs.destroy();
-    this.selection.off("new-node-front", this.onNewSelection);
-    this.selection.off("detached-front", this.onDetached);
-
-    let markupDestroyer = this._destroyMarkup();
-
-    let highlighterDestroyer = this.highlighters.destroy();
-    this.prefsObserver.destroy();
     this.reflowTracker.destroy();
     this.styleChangeTracker.destroy();
     this.search.destroy();
 
     this._toolbox = null;
     this.breadcrumbs = null;
     this.highlighters = null;
     this.is3PaneModeEnabled = null;
     this.panelDoc = null;
     this.panelWin.inspector = null;
     this.panelWin = null;
-    this.prefsObserver = null;
     this.resultsLength = null;
     this.search = null;
     this.searchBox = null;
     this.show3PaneToggle = null;
+    this.show3PaneTooltip = null;
     this.sidebar = null;
     this.store = null;
     this.target = null;
+    this.threePaneTooltip = null;
 
     this._panelDestroyer = promise.all([
       highlighterDestroyer,
       cssPropertiesDestroyer,
       markupDestroyer,
       sidebarDestroyer,
       ruleViewSideBarDestroyer
     ]);
--- a/devtools/client/inspector/shared/moz.build
+++ b/devtools/client/inspector/shared/moz.build
@@ -6,13 +6,14 @@
 
 DevToolsModules(
     'dom-node-preview.js',
     'highlighters-overlay.js',
     'node-types.js',
     'reflow-tracker.js',
     'style-change-tracker.js',
     'style-inspector-menu.js',
+    'three-pane-onboarding-tooltip.js',
     'tooltips-overlay.js',
     'utils.js'
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/shared/three-pane-onboarding-tooltip.js
@@ -0,0 +1,110 @@
+/* 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 = require("Services");
+const { openWebLink } = require("devtools/client/shared/link");
+const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+const SHOW_THREE_PANE_ONBOARDING_PREF = "devtools.inspector.show-three-pane-tooltip";
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const CONTAINER_WIDTH = 300;
+const LEARN_MORE_LINK = "https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/Use_the_3-pane_inspector?utm_source=devtools&utm_medium=3-pane-onboarding";
+
+/**
+ * Three pane inspector onboarding tooltip that is shown on the 3 pane inspector toggle
+ * button when the pref is on.
+ */
+class ThreePaneOnboardingTooltip {
+  constructor(toolbox, doc) {
+    this.toolbox = toolbox;
+    this.doc = doc;
+    this.tooltip = new HTMLTooltip(this.toolbox.doc, {
+      type: "arrow",
+      useXulWrapper: true,
+    });
+
+    this.onCloseButtonClick = this.onCloseButtonClick.bind(this);
+    this.onLearnMoreLinkClick = this.onLearnMoreLinkClick.bind(this);
+
+    const container = doc.createElementNS(XHTML_NS, "div");
+    container.className = "three-pane-onboarding-container";
+
+    const icon = doc.createElementNS(XHTML_NS, "span");
+    icon.className = "three-pane-onboarding-icon";
+    container.appendChild(icon);
+
+    const content = doc.createElementNS(XHTML_NS, "div");
+    content.className = "three-pane-onboarding-content";
+    container.appendChild(content);
+
+    const message = doc.createElementNS(XHTML_NS, "div");
+    const learnMoreString = L10N.getStr("inspector.threePaneOnboarding.learnMoreLink");
+    const messageString = L10N.getFormatStr("inspector.threePaneOnboarding.content",
+      learnMoreString);
+    const learnMoreStartIndex = messageString.indexOf(learnMoreString);
+
+    message.append(messageString.substring(0, learnMoreStartIndex));
+
+    this.learnMoreLink = doc.createElementNS(XHTML_NS, "a");
+    this.learnMoreLink.className = "three-pane-onboarding-link";
+    this.learnMoreLink.href = "#";
+    this.learnMoreLink.textContent = learnMoreString;
+
+    message.append(this.learnMoreLink);
+    message.append(messageString.substring(learnMoreStartIndex + learnMoreString.length));
+    content.append(message);
+
+    this.closeButton = doc.createElementNS(XHTML_NS, "button");
+    this.closeButton.className = "three-pane-onboarding-close-button devtools-button";
+    container.appendChild(this.closeButton);
+
+    this.closeButton.addEventListener("click", this.onCloseButtonClick);
+    this.learnMoreLink.addEventListener("click", this.onLearnMoreLinkClick);
+
+    this.tooltip.setContent(container, { width: CONTAINER_WIDTH });
+    this.tooltip.show(this.doc.querySelector("#inspector-sidebar .sidebar-toggle"), {
+      position: "top",
+    });
+  }
+
+  destroy() {
+    this.closeButton.removeEventListener("click", this.onCloseButtonClick);
+    this.learnMoreLink.removeEventListener("click", this.onLearnMoreLinkClick);
+
+    this.tooltip.destroy();
+
+    this.closeButton = null;
+    this.doc = null;
+    this.learnMoreLink = null;
+    this.toolbox = null;
+    this.tooltip = null;
+  }
+
+  /**
+   * Handler for the "click" event on the close button. Hides the onboarding tooltip
+   * and sets the show three pane onboarding tooltip pref to false.
+   */
+  onCloseButtonClick() {
+    Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false);
+    this.tooltip.hide();
+  }
+
+  /**
+   * Handler for the "click" event on the learn more button. Hides the onboarding tooltip
+   * and opens the link to the mdn page in a new tab.
+   */
+  onLearnMoreLinkClick() {
+    Services.prefs.setBoolPref(SHOW_THREE_PANE_ONBOARDING_PREF, false);
+    this.tooltip.hide();
+    openWebLink(LEARN_MORE_LINK);
+  }
+}
+
+module.exports = ThreePaneOnboardingTooltip;
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -112,16 +112,17 @@ devtools.jar:
     skin/toolbars.css (themes/toolbars.css)
     skin/toolbox.css (themes/toolbox.css)
     skin/tooltips.css (themes/tooltips.css)
     skin/images/accessibility.svg (themes/images/accessibility.svg)
     skin/images/add.svg (themes/images/add.svg)
     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
+    skin/images/fox-smiling.svg (themes/images/fox-smiling.svg)
     skin/images/grid.svg (themes/images/grid.svg)
     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/controls.png (themes/images/controls.png)
     skin/images/controls@2x.png (themes/images/controls@2x.png)
     skin/images/copy.svg (themes/images/copy.svg)
     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
     skin/images/performance-details-waterfall.svg (themes/images/performance-details-waterfall.svg)
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -459,8 +459,16 @@ inspector.classPanel.newClass.placeholde
 # LOCALIZATION NOTE (inspector.classPanel.noClasses): This is the text displayed in the
 # class panel when the current element has no classes applied.
 inspector.classPanel.noClasses=No classes on this element
 
 # LOCALIZATION NOTE (inspector.noProperties): In the case where there are no CSS
 # properties to display e.g. due to search criteria this message is
 # displayed.
 inspector.noProperties=No CSS properties found.
+
+# LOCALIZATION NOTE (inspector.threePaneOnboarding.content,
+# inspector.threePaneOnboarding.learnMoreLink): This is the content shown in the 3 pane
+# inspector onboarding tooltip that is displayed on top of the 3 pane inspector toggle
+# button. %S in the content will be replaced by a link at run time with the learnMoreLink
+# string.
+inspector.threePaneOnboarding.content=New: 3-pane mode lets you see both CSS rules and Layout tools. Click this button to toggle. %S
+inspector.threePaneOnboarding.learnMoreLink=Learn more
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -52,16 +52,22 @@ pref("devtools.inspector.remote", false)
 // Enable the 3 pane mode toggle in the inspector
 #if defined(NIGHTLY_BUILD)
 pref("devtools.inspector.three-pane-toggle", true);
 #else
 pref("devtools.inspector.three-pane-toggle", false);
 #endif
 // Enable the 3 pane mode in the inspector
 pref("devtools.inspector.three-pane-enabled", false);
+// Show the 3 pane onboarding tooltip in the inspector
+#if defined(NIGHTLY_BUILD)
+pref("devtools.inspector.show-three-pane-tooltip", true);
+#else
+pref("devtools.inspector.show-three-pane-tooltip", false);
+#endif
 // Collapse pseudo-elements by default in the rule-view
 pref("devtools.inspector.show_pseudo_elements", false);
 // The default size for image preview tooltips in the rule-view/computed-view/markup-view
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
 // Show all native anonymous content (like controls in <video> tags)
 pref("devtools.inspector.showAllAnonymousContent", false);
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -103,18 +103,20 @@ function loadFrameScriptUtils(browser = 
   info("Loading the helper frame script " + frameURL);
   mm.loadFrameScript(frameURL, false);
   SimpleTest.registerCleanupFunction(() => {
     mm = null;
   });
   return mm;
 }
 
+Services.prefs.setBoolPref("devtools.inspector.show-three-pane-tooltip", false);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.dump.emit");
+  Services.prefs.clearUserPref("devtools.inspector.show-three-pane-tooltip");
   Services.prefs.clearUserPref("devtools.toolbox.host");
   Services.prefs.clearUserPref("devtools.toolbox.previousHost");
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
 });
 
 registerCleanupFunction(async function cleanup() {
   while (gBrowser.tabs.length > 1) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/fox-smiling.svg
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="72px" height="72px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
+<path fill="#FFD8CA" d="M30.4,61C15.5,60,7.7,54.7,0.3,41.4C0.1,41.1,0,40.7,0,40.4h8.5l0-3.2h0c0-0.2,0-0.4,0-0.6
+	c0-4.9,3.9-8.8,8.8-8.8c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.4,0,0.6h0v15.7C26.1,56.2,27.8,59.2,30.4,61z M63.5,40.4v-3.2h0
+	c0-0.2,0-0.5,0-0.7c0-4.9-3.9-8.8-8.8-8.8c-4.8,0-8.7,3.8-8.8,8.5v0.5c0,0.2,0,0.3,0,0.5h0v15.7c0,3.4-1.7,6.3-4.3,8.1
+	c14.9-1,22.7-6.2,30.1-19.5c0.2-0.3,0.3-0.7,0.2-1.1H63.5z"/>
+<path fill="#FF6F1F" d="M42.5,60.3c0.1,0,0.1-0.1,0.2-0.1c0.2-0.2,0.4-0.4,0.6-0.6c0.1-0.1,0.1-0.1,0.2-0.2c0.2-0.2,0.4-0.5,0.6-0.7
+	c0,0,0.1-0.1,0.1-0.1c0.2-0.3,0.4-0.6,0.6-1c0,0,0,0,0,0c0.2-0.3,0.3-0.6,0.4-1c0-0.1,0.1-0.2,0.1-0.3c0.1-0.2,0.2-0.5,0.2-0.7
+	c0-0.1,0.1-0.2,0.1-0.4c0.1-0.3,0.1-0.5,0.1-0.8c0-0.1,0-0.2,0.1-0.3c0-0.4,0.1-0.8,0.1-1.1v-1.2V37.1h0c0-0.2,0-0.3,0-0.5v-0.5
+	c0.1-4.7,4-8.5,8.8-8.5c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.5,0,0.7h0v3.2H72c0-0.4-0.1-0.8-0.3-1.1c-2-3.7-4.1-7.2-6.3-10.4
+	c1.6-1.2,2.9-2.8,4-4.6c3-5.2,2.8-11.3,0.1-16.2c-0.2-0.3-0.5-0.5-0.9-0.5c-5.5,0-10.4,3-13.4,8.2c-0.4,0.6-0.7,1.3-1,1.9
+	C49.2,14.7,43.4,13,36,13s-13.2,1.8-18.1,4.8c-0.3-0.7-0.6-1.3-1-1.9c-3-5.2-7.9-8.2-13.4-8.2c-0.4,0-0.7,0.2-0.9,0.5
+	c-2.7,4.8-2.9,11,0.1,16.2c1.1,1.8,2.4,3.3,4,4.6C4.4,32,2.3,35.5,0.3,39.3C0.1,39.6,0,40,0,40.4h8.5l0-3.2h0c0-0.2,0-0.4,0-0.6
+	c0-4.9,3.9-8.8,8.8-8.8c4.9,0,8.8,3.9,8.8,8.8c0,0.2,0,0.4,0,0.6h0v14.5v1.2c0,0.4,0,0.8,0.1,1.1c0,0.1,0,0.2,0.1,0.3
+	c0,0.3,0.1,0.5,0.1,0.8c0,0.1,0.1,0.2,0.1,0.4c0.1,0.3,0.2,0.5,0.2,0.7c0,0.1,0.1,0.2,0.1,0.3c0.1,0.3,0.3,0.6,0.4,1c0,0,0,0,0,0
+	c0.2,0.3,0.4,0.7,0.6,1c0,0,0.1,0.1,0.1,0.1c0.2,0.3,0.4,0.5,0.6,0.7c0.1,0.1,0.1,0.1,0.2,0.2c0.2,0.2,0.4,0.4,0.6,0.6
+	c0.1,0,0.1,0.1,0.2,0.1c0.3,0.2,0.6,0.5,0.9,0.7c1.6,1.1,3.5,1.7,5.6,1.7c2.1,0,4-0.6,5.6-1.7C41.9,60.8,42.2,60.6,42.5,60.3z"/>
+<g>
+	<path fill="#2B3B47" d="M32.9,45.7c0,0,0.4,0,1,0c0.6,0,1.3,0,2.1,0c0.8,0,1.5,0,2.1,0c0.6,0,1-0.1,1-0.1c1.2-0.1,2.3,0.8,2.4,2.1
+		c0.1,0.6-0.2,1.3-0.6,1.7l-1.9,2.1c0,0-0.4,0.4-1,0.7c-0.3,0.2-0.6,0.3-1,0.4c-0.2,0.1-0.4,0.1-0.6,0.1c-0.2,0-0.4,0-0.6,0
+		c-0.2,0-0.4,0-0.6,0c-0.2,0-0.4-0.1-0.6-0.1c-0.2,0-0.4-0.1-0.5-0.2c-0.2,0-0.3-0.1-0.5-0.2c-0.6-0.3-1-0.6-1-0.6l-1.8-1.8
+		c-0.9-0.9-0.9-2.4,0-3.3C31.6,45.9,32.3,45.6,32.9,45.7z"/>
+</g>
+<path fill="#2B3B47" stroke="#2B3B47" stroke-width="1.0904" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
+	M30.6,55c0,0,2.1,1.3,5.4,1.3s5.4-1.3,5.4-1.3s-0.3,4.9-5.4,4.9S30.6,55,30.6,55z"/>
+<path fill="#2B3B47" d="M17.3,32.5c1.7,0,3,1.3,3,3v6.8c0,1.7-1.3,3-3,3l0,0c-1.7,0-3-1.3-3-3v-6.8C14.3,33.8,15.7,32.5,17.3,32.5
+	L17.3,32.5z"/>
+<path fill="#2B3B47" d="M54.8,32.5c1.7,0,3,1.3,3,3v6.8c0,1.7-1.3,3-3,3l0,0c-1.7,0-3-1.3-3-3v-6.8C51.8,33.8,53.1,32.5,54.8,32.5
+	L54.8,32.5z"/>
+<path fill="#2B3B47" d="M57.3,20.1c3.6-8,10-11.5,11.5-12.3c0,0,0-0.1,0-0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.3,0-0.4,0
+	c-0.2,0-0.4,0-0.6,0c-0.1,0-0.1,0-0.2,0c-5.1,0.4-9.5,3.3-12.3,8.1c-0.4,0.6-0.7,1.3-1,1.9C55.2,18.5,56.3,19.2,57.3,20.1z"/>
+<path fill="#2B3B47" d="M14.7,20.1c1.1-0.9,2.3-1.8,3.6-2.5c-0.2-0.7-0.5-1.3-0.9-1.8c-3.1-5.2-8.6-8.1-14.2-8.1
+	c-0.1,0-0.1,0.1,0,0.2C4.7,8.5,11.1,12.1,14.7,20.1z"/>
+</svg>
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -8,21 +8,25 @@
 @import url(chrome://devtools/content/shared/widgets/filter-widget.css);
 @import url(chrome://devtools/content/shared/widgets/spectrum.css);
 
 /* Tooltip specific theme variables */
 
 .theme-dark {
   --bezier-diagonal-color: #eee;
   --bezier-grid-color: rgba(0, 0, 0, 0.2);
+  --onboarding-link-color: var(--theme-highlight-blue);
+  --onboarding-link-active-color: var(--blue-40);
 }
 
 .theme-light {
   --bezier-diagonal-color: rgba(0, 0, 0, 0.2);
   --bezier-grid-color: rgba(0, 0, 0, 0.05);
+  --onboarding-link-color: var(--blue-60);
+  --onboarding-link-active-color: var(--blue-70);
 }
 
 /* Tooltip widget (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
 
 .devtools-tooltip .panel-arrowcontent {
   padding: 4px;
 }
 
@@ -475,8 +479,58 @@
 
 /* Tooltip: Image tooltip */
 
 .devtools-tooltip-image-broken {
   box-sizing: border-box;
   height: 100%;
   padding: 7px;
 }
+
+/* Tooltip: 3 Pane Inspecot Onboarding Tooltip */
+
+.three-pane-onboarding-container {
+  align-items: center;
+  background-color: var(--theme-toolbar-background);
+  box-sizing: border-box;
+  color: var(--theme-body-color);
+  display: flex;
+  font-size: 12px;
+  padding: 7px;
+  width: 100%;
+  -moz-user-select: none;
+}
+
+.three-pane-onboarding-icon {
+  display: inline-block;
+  background-size: 21px;
+  width: 21px;
+  height: 21px;
+  margin: 8px;
+  background-image: url("chrome://devtools/skin/images/fox-smiling.svg");
+}
+
+.three-pane-onboarding-content {
+  flex: 1;
+  padding-inline-start: 5px;
+}
+
+.three-pane-onboarding-link {
+  color: var(--onboarding-link-color);
+  cursor: pointer;
+}
+
+.three-pane-onboarding-link:hover {
+  text-decoration: underline;
+}
+
+.three-pane-onboarding-link:active {
+  color: var(--onboarding-link-active-color);
+}
+
+.three-pane-onboarding-close-button {
+  align-self: flex-start;
+}
+
+.three-pane-onboarding-close-button::before {
+  background-image: url("chrome://devtools/skin/images/close.svg");
+  margin: -6px 0 0 -6px;
+}
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -209,16 +209,17 @@
   --animation-curve: cubic-bezier(.07,.95,0,1);
 
   /* Firefox Colors CSS Variables v1.0.3
    * Colors are taken from: https://github.com/FirefoxUX/design-tokens */
   --magenta-65: #dd00a9;
 
   --purple-60: #8000d7;
 
+  --blue-40: #45a1ff;
   --blue-50: #0a84ff;
   --blue-55: #0074e8;
   --blue-60: #0060df;
   --blue-70: #003eaa;
 
   --red-70: #a4000f;
 
   --green-50: #30e60b;