Bug 1320939 - Lazy load all tooltip widgets until each is really used. r=jdescottes draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Tue, 31 Jan 2017 15:31:37 +0100
changeset 561302 5335eacc8d7cf2660352db403aa70387558f13b2
parent 561301 6cde13bbe50685a486a735d0060acf0e57a573c5
child 623943 19306949fbb331840d1cf91c7ffcd2611a0f19fa
push id53696
push userbmo:poirot.alex@gmail.com
push dateWed, 12 Apr 2017 13:40:01 +0000
reviewersjdescottes
bugs1320939
milestone55.0a1
Bug 1320939 - Lazy load all tooltip widgets until each is really used. r=jdescottes MozReview-Commit-ID: 9P93GocdLm8
devtools/client/inspector/computed/computed.js
devtools/client/inspector/rules/rules.js
devtools/client/inspector/rules/test/browser_rules_authored_color.js
devtools/client/inspector/rules/test/browser_rules_colorUnit.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-and-image-tooltip_01.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-and-image-tooltip_02.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-appears-on-swatch-click.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-commit-on-ENTER.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-edit-gradient.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-hides-on-tooltip.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-multiple-changes.js
devtools/client/inspector/rules/test/browser_rules_colorpicker-release-outside-frame.js
devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-02.js
devtools/client/inspector/rules/test/browser_rules_css-docs-tooltip_closes-on-escape.js
devtools/client/inspector/rules/test/browser_rules_cubicbezier-appears-on-swatch-click.js
devtools/client/inspector/rules/test/browser_rules_cubicbezier-commit-on-ENTER.js
devtools/client/inspector/rules/test/browser_rules_cubicbezier-revert-on-ESC.js
devtools/client/inspector/rules/test/browser_rules_edit-value-after-name_03.js
devtools/client/inspector/rules/test/browser_rules_eyedropper.js
devtools/client/inspector/rules/test/browser_rules_filtereditor-appears-on-swatch-click.js
devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js
devtools/client/inspector/rules/test/browser_rules_filtereditor-revert-on-ESC.js
devtools/client/inspector/rules/test/browser_rules_user-agent-styles-uneditable.js
devtools/client/inspector/rules/test/head.js
devtools/client/inspector/rules/views/text-property-editor.js
devtools/client/inspector/shared/style-inspector-menu.js
devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_02.js
devtools/client/inspector/shared/test/browser_styleinspector_tooltip-background-image.js
devtools/client/inspector/shared/test/browser_styleinspector_tooltip-closes-on-new-selection.js
devtools/client/inspector/shared/test/browser_styleinspector_tooltip-longhand-fontfamily.js
devtools/client/inspector/shared/test/browser_styleinspector_tooltip-multiple-background-images.js
devtools/client/inspector/shared/test/browser_styleinspector_tooltip-shorthand-fontfamily.js
devtools/client/inspector/shared/test/browser_styleinspector_tooltip-size.js
devtools/client/inspector/shared/tooltips-overlay.js
devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
devtools/client/shared/widgets/tooltip/CssDocsTooltip.js
devtools/client/shared/widgets/tooltip/HTMLTooltip.js
devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -218,17 +218,16 @@ function CssComputedView(inspector, docu
 
   this.createBoxModelView();
   this.createStyleViews();
 
   this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
 
   // Add the tooltips and highlightersoverlay
   this.tooltips = new TooltipsOverlay(this);
-  this.tooltips.addToView();
 
   this.highlighters.addToView(this);
 }
 
 /**
  * Lookup a l10n string in the shared styleinspector string bundle.
  *
  * @param {String} name
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -176,17 +176,16 @@ function CssRuleView(inspector, document
   });
 
   this._showEmpty();
 
   this._contextmenu = new StyleInspectorMenu(this, { isRuleView: true });
 
   // Add the tooltips and highlighters to the view
   this.tooltips = new TooltipsOverlay(this);
-  this.tooltips.addToView();
 
   this.highlighters.addToView(this);
 
   this.classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
 
   EventEmitter.decorate(this);
 }
 
--- a/devtools/client/inspector/rules/test/browser_rules_authored_color.js
+++ b/devtools/client/inspector/rules/test/browser_rules_authored_color.js
@@ -28,22 +28,22 @@ add_task(function* () {
     html += `<div id="${id}" style="color: ${color}">Styled Node</div>`;
   }
 
   let tab = yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(html));
 
   let {inspector, view} = yield openRuleView();
 
   for (let color of colors) {
-    let cPicker = view.tooltips.colorPicker;
     let selector = "#" + color.id;
     yield selectNode(selector, inspector);
 
     let swatch = getRuleViewProperty(view, "element", "color").valueSpan
         .querySelector(".ruleview-colorswatch");
+    let cPicker = view.tooltips.getTooltip("colorPicker");
     let onColorPickerReady = cPicker.once("ready");
     swatch.click();
     yield onColorPickerReady;
 
     yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], {
       selector,
       name: "color",
       value: "rgb(0, 255, 0)"
--- a/devtools/client/inspector/rules/test/browser_rules_colorUnit.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorUnit.js
@@ -34,17 +34,17 @@ add_task(function* () {
 
     let target = TargetFactory.forTab(tab);
     yield gDevTools.closeToolbox(target);
     gBrowser.removeCurrentTab();
   }
 });
 
 function* basicTest(view, name, result) {
-  let cPicker = view.tooltips.colorPicker;
+  let cPicker = view.tooltips.getTooltip("colorPicker");
   let swatch = getRuleViewProperty(view, "#testid", "color").valueSpan
       .querySelector(".ruleview-colorswatch");
   let onColorPickerReady = cPicker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   yield simulateColorPickerChange(view, cPicker, [0, 255, 0, 1], {
     selector: "#testid",
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-and-image-tooltip_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-and-image-tooltip_01.js
@@ -22,24 +22,24 @@ add_task(function* () {
   let {view} = yield openRuleView();
   let value = getRuleViewProperty(view, "body", "background").valueSpan;
   let swatch = value.querySelectorAll(".ruleview-colorswatch")[0];
   let url = value.querySelector(".theme-link");
   yield testImageTooltipAfterColorChange(swatch, url, view);
 });
 
 function* testImageTooltipAfterColorChange(swatch, url, ruleView) {
+  let previewTooltip = ruleView.tooltips.getTooltip("previewTooltip");
   info("First, verify that the image preview tooltip works");
-  let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip,
-                                          url);
+  let anchor = yield isHoverTooltipTarget(previewTooltip, url);
   ok(anchor, "The image preview tooltip is shown on the url span");
   is(anchor, url, "The anchor returned by the showOnHover callback is correct");
 
   info("Open the color picker tooltip and change the color");
-  let picker = ruleView.tooltips.colorPicker;
+  let picker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = picker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   yield simulateColorPickerChange(ruleView, picker, [0, 0, 0, 1], {
     selector: "body",
     name: "background-image",
     value: 'url("chrome://global/skin/icons/warning-64.png"), linear-gradient(rgb(0, 0, 0), rgb(255, 0, 102) 400px)'
@@ -52,12 +52,12 @@ function* testImageTooltipAfterColorChan
   yield onHidden;
   yield onModifications;
 
   info("Verify again that the image preview tooltip works");
   // After a color change, the property is re-populated, we need to get the new
   // dom node
   url = getRuleViewProperty(ruleView, "body", "background").valueSpan
     .querySelector(".theme-link");
-  anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip, url);
+  anchor = yield isHoverTooltipTarget(previewTooltip, url);
   ok(anchor, "The image preview tooltip is shown on the url span");
   is(anchor, url, "The anchor returned by the showOnHover callback is correct");
 }
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-and-image-tooltip_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-and-image-tooltip_02.js
@@ -26,17 +26,17 @@ add_task(function* () {
   yield testColorChangeIsntRevertedWhenOtherTooltipIsShown(view);
 });
 
 function* testColorChangeIsntRevertedWhenOtherTooltipIsShown(ruleView) {
   let swatch = getRuleViewProperty(ruleView, "body", "background").valueSpan
     .querySelector(".ruleview-colorswatch");
 
   info("Open the color picker tooltip and change the color");
-  let picker = ruleView.tooltips.colorPicker;
+  let picker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = picker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   yield simulateColorPickerChange(ruleView, picker, [0, 0, 0, 1], {
     selector: "body",
     name: "background-color",
     value: "rgb(0, 0, 0)"
@@ -48,19 +48,20 @@ function* testColorChangeIsntRevertedWhe
   let onHidden = picker.tooltip.once("hidden");
   focusAndSendKey(spectrum.element.ownerDocument.defaultView, "RETURN");
   yield onHidden;
   yield onModifications;
 
   info("Open the image preview tooltip");
   let value = getRuleViewProperty(ruleView, "body", "background").valueSpan;
   let url = value.querySelector(".theme-link");
-  let onShown = ruleView.tooltips.previewTooltip.once("shown");
-  let anchor = yield isHoverTooltipTarget(ruleView.tooltips.previewTooltip, url);
-  ruleView.tooltips.previewTooltip.show(anchor);
+  let previewTooltip = ruleView.tooltips.getTooltip("previewTooltip");
+  let onShown = previewTooltip.once("shown");
+  let anchor = yield isHoverTooltipTarget(previewTooltip, url);
+  previewTooltip.show(anchor);
   yield onShown;
 
   info("Image tooltip is shown, verify that the swatch is still correct");
   swatch = value.querySelector(".ruleview-colorswatch");
   is(swatch.style.backgroundColor, "black",
     "The swatch's color is correct");
   is(swatch.nextSibling.textContent, "black", "The color name is correct");
 }
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-appears-on-swatch-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-appears-on-swatch-click.js
@@ -28,17 +28,17 @@ add_task(function* () {
     info("Testing that the colorpicker appears on swatch click");
     let value = getRuleViewProperty(view, "body", property).valueSpan;
     let swatch = value.querySelector(".ruleview-colorswatch");
     yield testColorPickerAppearsOnColorSwatchClick(view, swatch);
   }
 });
 
 function* testColorPickerAppearsOnColorSwatchClick(view, swatch) {
-  let cPicker = view.tooltips.colorPicker;
+  let cPicker = view.tooltips.getTooltip("colorPicker");
   ok(cPicker, "The rule-view has the expected colorPicker property");
 
   let cPickerPanel = cPicker.tooltip.panel;
   ok(cPickerPanel, "The XUL panel for the color picker exists");
 
   let onColorPickerReady = cPicker.once("ready");
   swatch.click();
   yield onColorPickerReady;
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-commit-on-ENTER.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-commit-on-ENTER.js
@@ -21,17 +21,17 @@ add_task(function* () {
   let {view} = yield openRuleView();
 
   let swatch = getRuleViewProperty(view, "body", "border").valueSpan
     .querySelector(".ruleview-colorswatch");
   yield testPressingEnterCommitsChanges(swatch, view);
 });
 
 function* testPressingEnterCommitsChanges(swatch, ruleView) {
-  let cPicker = ruleView.tooltips.colorPicker;
+  let cPicker = ruleView.tooltips.getTooltip("colorPicker");
 
   let onColorPickerReady = cPicker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   yield simulateColorPickerChange(ruleView, cPicker, [0, 255, 0, .5], {
     selector: "body",
     name: "border-left-color",
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-edit-gradient.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-edit-gradient.js
@@ -47,17 +47,17 @@ function testColorParsing(view) {
 
 function* testPickingNewColor(view) {
   // Grab the first color swatch and color in the gradient
   let ruleEl = getRuleViewProperty(view, "body", "background-image");
   let swatchEl = ruleEl.valueSpan.querySelector(".ruleview-colorswatch");
   let colorEl = ruleEl.valueSpan.querySelector(".ruleview-color");
 
   info("Get the color picker tooltip and clicking on the swatch to show it");
-  let cPicker = view.tooltips.colorPicker;
+  let cPicker = view.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = cPicker.once("ready");
   swatchEl.click();
   yield onColorPickerReady;
 
   let change = {
     selector: "body",
     name: "background-image",
     value: "linear-gradient(to left, rgb(1, 1, 1) 25%, " +
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-hides-on-tooltip.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-hides-on-tooltip.js
@@ -23,24 +23,24 @@ add_task(function* () {
   let {view} = yield openRuleView();
 
   let swatch = getRuleViewProperty(view, "body", "color").valueSpan
     .querySelector(".ruleview-colorswatch");
 
   let bgImageSpan = getRuleViewProperty(view, "body", "background-image").valueSpan;
   let uriSpan = bgImageSpan.querySelector(".theme-link");
 
-  let colorPicker = view.tooltips.colorPicker;
+  let colorPicker = view.tooltips.getTooltip("colorPicker");
   info("Showing the color picker tooltip by clicking on the color swatch");
   let onColorPickerReady = colorPicker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   info("Now showing the image preview tooltip to hide the color picker");
   let onHidden = colorPicker.tooltip.once("hidden");
   // Hiding the color picker refreshes the value.
   let onRuleViewChanged = view.once("ruleview-changed");
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), uriSpan);
   yield onHidden;
   yield onRuleViewChanged;
 
   ok(true, "The color picker closed when the image preview tooltip appeared");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-multiple-changes.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-multiple-changes.js
@@ -37,17 +37,17 @@ add_task(function* () {
 function* testSimpleMultipleColorChanges(inspector, ruleView) {
   yield selectNode("p", inspector);
 
   info("Getting the <p> tag's color property");
   let swatch = getRuleViewProperty(ruleView, "p", "color").valueSpan
     .querySelector(".ruleview-colorswatch");
 
   info("Opening the color picker");
-  let picker = ruleView.tooltips.colorPicker;
+  let picker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = picker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   info("Changing the color several times");
   let colors = [
     {rgba: [0, 0, 0, 1], computed: "rgb(0, 0, 0)"},
     {rgba: [100, 100, 100, 1], computed: "rgb(100, 100, 100)"},
@@ -65,17 +65,17 @@ function* testSimpleMultipleColorChanges
 function* testComplexMultipleColorChanges(inspector, ruleView) {
   yield selectNode("body", inspector);
 
   info("Getting the <body> tag's color property");
   let swatch = getRuleViewProperty(ruleView, "body", "background").valueSpan
     .querySelector(".ruleview-colorswatch");
 
   info("Opening the color picker");
-  let picker = ruleView.tooltips.colorPicker;
+  let picker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = picker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   info("Changing the color several times");
   let colors = [
     {rgba: [0, 0, 0, 1], computed: "rgb(0, 0, 0)"},
     {rgba: [100, 100, 100, 1], computed: "rgb(100, 100, 100)"},
@@ -96,17 +96,17 @@ function* testComplexMultipleColorChange
 function* testOverriddenMultipleColorChanges(inspector, ruleView) {
   yield selectNode("p", inspector);
 
   info("Getting the <body> tag's color property");
   let swatch = getRuleViewProperty(ruleView, "body", "color").valueSpan
     .querySelector(".ruleview-colorswatch");
 
   info("Opening the color picker");
-  let picker = ruleView.tooltips.colorPicker;
+  let picker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = picker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   info("Changing the color several times");
   let colors = [
     {rgba: [0, 0, 0, 1], computed: "rgb(0, 0, 0)"},
     {rgba: [100, 100, 100, 1], computed: "rgb(100, 100, 100)"},
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-release-outside-frame.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-release-outside-frame.js
@@ -46,17 +46,17 @@ add_task(function* () {
   EventUtils.synthesizeMouse(spectrum.dragger, 10, 10, {
     // -1 = no buttons are pressed down
     button: -1,
     type: "mousemove",
   }, spectrum.dragger.ownerDocument.defaultView);
 });
 
 function* openColorPickerForSwatch(swatch, view) {
-  let cPicker = view.tooltips.colorPicker;
+  let cPicker = view.tooltips.getTooltip("colorPicker");
   ok(cPicker, "The rule-view has the expected colorPicker property");
 
   let cPickerPanel = cPicker.tooltip.panel;
   ok(cPickerPanel, "The XUL panel for the color picker exists");
 
   let onColorPickerReady = cPicker.once("ready");
   swatch.click();
   yield onColorPickerReady;
--- a/devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-02.js
@@ -42,17 +42,17 @@ add_task(function* () {
   info("Setting the popupNode for the MDN docs tooltip");
 
   let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
 
   let allMenuItems = openStyleContextMenuAndGetAllItems(view, nameSpan.firstChild);
   let menuitemShowMdnDocs = allMenuItems.find(item => item.label ===
     STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.showMdnDocs"));
 
-  let cssDocs = view.tooltips.cssDocs;
+  let cssDocs = view.tooltips.getTooltip("cssDocs");
 
   info("Showing the MDN docs tooltip");
   let onShown = cssDocs.tooltip.once("shown");
   menuitemShowMdnDocs.click();
   yield onShown;
   ok(true, "The MDN docs tooltip was shown");
 
   info("Quick check that the tooltip contents are set");
--- a/devtools/client/inspector/rules/test/browser_rules_css-docs-tooltip_closes-on-escape.js
+++ b/devtools/client/inspector/rules/test/browser_rules_css-docs-tooltip_closes-on-escape.js
@@ -33,19 +33,20 @@ add_task(function* () {
   yield selectNode("div", inspector);
 
   setBaseCssDocsUrl(URL_ROOT);
 
   info("Retrieve a valid anchor for the CssDocs tooltip");
   let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
 
   info("Showing the MDN docs tooltip");
-  let onShown = view.tooltips.cssDocs.tooltip.once("shown");
-  view.tooltips.cssDocs.show(nameSpan, PROPERTYNAME);
+  let cssDocs = view.tooltips.getTooltip("cssDocs");
+  let onShown = cssDocs.tooltip.once("shown");
+  cssDocs.show(nameSpan, PROPERTYNAME);
   yield onShown;
   ok(true, "The MDN docs tooltip was shown");
 
   info("Simulate pressing the 'Escape' key");
-  let onHidden = view.tooltips.cssDocs.tooltip.once("hidden");
+  let onHidden = cssDocs.tooltip.once("hidden");
   EventUtils.sendKey("escape");
   yield onHidden;
   ok(true, "The MDN docs tooltip was hidden on pressing 'escape'");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_cubicbezier-appears-on-swatch-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_cubicbezier-appears-on-swatch-click.js
@@ -48,17 +48,17 @@ add_task(function* () {
     info("Testing that the cubic-bezier appears on cubicswatch click");
     yield testAppears(view, swatch);
   }
 });
 
 function* testAppears(view, swatch) {
   ok(swatch, "The cubic-swatch exists");
 
-  let bezier = view.tooltips.cubicBezier;
+  let bezier = view.tooltips.getTooltip("cubicBezier");
   ok(bezier, "The rule-view has the expected cubicBezier property");
 
   let bezierPanel = bezier.tooltip.panel;
   ok(bezierPanel, "The XUL panel for the cubic-bezier tooltip exists");
 
   let onBezierWidgetReady = bezier.once("ready");
   swatch.click();
   yield onBezierWidgetReady;
--- a/devtools/client/inspector/rules/test/browser_rules_cubicbezier-commit-on-ENTER.js
+++ b/devtools/client/inspector/rules/test/browser_rules_cubicbezier-commit-on-ENTER.js
@@ -22,17 +22,17 @@ add_task(function* () {
   info("Getting the bezier swatch element");
   let swatch = getRuleViewProperty(view, "body", "transition").valueSpan
     .querySelector(".ruleview-bezierswatch");
 
   yield testPressingEnterCommitsChanges(swatch, view);
 });
 
 function* testPressingEnterCommitsChanges(swatch, ruleView) {
-  let bezierTooltip = ruleView.tooltips.cubicBezier;
+  let bezierTooltip = ruleView.tooltips.getTooltip("cubicBezier");
 
   info("Showing the tooltip");
   let onBezierWidgetReady = bezierTooltip.once("ready");
   swatch.click();
   yield onBezierWidgetReady;
 
   let widget = yield bezierTooltip.widget;
   info("Simulating a change of curve in the widget");
--- a/devtools/client/inspector/rules/test/browser_rules_cubicbezier-revert-on-ESC.js
+++ b/devtools/client/inspector/rules/test/browser_rules_cubicbezier-revert-on-ESC.js
@@ -85,16 +85,16 @@ function* getRulePropertyValue(name) {
     name: name
   });
   return propValue;
 }
 
 function* escapeTooltip(view) {
   info("Pressing ESCAPE to close the tooltip");
 
-  let bezierTooltip = view.tooltips.cubicBezier;
+  let bezierTooltip = view.tooltips.getTooltip("cubicBezier");
   let widget = yield bezierTooltip.widget;
   let onHidden = bezierTooltip.tooltip.once("hidden");
   let onModifications = view.once("ruleview-changed");
   focusAndSendKey(widget.parent.ownerDocument.defaultView, "ESCAPE");
   yield onHidden;
   yield onModifications;
 }
--- a/devtools/client/inspector/rules/test/browser_rules_edit-value-after-name_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-value-after-name_03.js
@@ -28,17 +28,17 @@ add_task(function* () {
 
   info("Test click on color swatch while editing property name");
 
   yield selectNode("#testid", inspector);
   let ruleEditor = getRuleViewRuleEditor(view, 1);
   let propEditor = ruleEditor.rule.textProps[1].editor;
   let swatchSpan = propEditor.valueSpan.querySelectorAll(
     ".ruleview-colorswatch")[3];
-  let colorPicker = view.tooltips.colorPicker;
+  let colorPicker = view.tooltips.getTooltip("colorPicker");
 
   info("Focus the background name span");
   yield focusEditableField(view, propEditor.nameSpan);
   let editor = inplaceEditor(propEditor.doc.activeElement);
 
   info("Modify the background property to background-image to trigger the " +
     "property-value-updated event");
   editor.input.value = "background-image";
--- a/devtools/client/inspector/rules/test/browser_rules_eyedropper.js
+++ b/devtools/client/inspector/rules/test/browser_rules_eyedropper.js
@@ -43,17 +43,17 @@ add_task(function* () {
   info("Get the background-color property from the rule-view");
   let property = getRuleViewProperty(view, "#div2", "background-color");
   let swatch = property.valueSpan.querySelector(".ruleview-colorswatch");
   ok(swatch, "Color swatch is displayed for the bg-color property");
 
   info("Open the eyedropper from the colorpicker tooltip");
   yield openEyedropper(view, swatch);
 
-  let tooltip = view.tooltips.colorPicker.tooltip;
+  let tooltip = view.tooltips.getTooltip("colorPicker").tooltip;
   ok(!tooltip.isVisible(), "color picker tooltip is closed after opening eyedropper");
 
   info("Test that pressing escape dismisses the eyedropper");
   yield testESC(swatch, inspector, testActor);
 
   info("Open the eyedropper again");
   yield openEyedropper(view, swatch);
 
@@ -102,20 +102,20 @@ function* testSelect(view, swatch, inspe
   is(color, EXPECTED_COLOR, "swatch changed colors");
 
   is((yield getComputedStyleProperty("div", null, "background-color")),
      EXPECTED_COLOR,
      "div's color set to body color after dropper");
 }
 
 function* openEyedropper(view, swatch) {
-  let tooltip = view.tooltips.colorPicker.tooltip;
+  let tooltip = view.tooltips.getTooltip("colorPicker").tooltip;
 
   info("Click on the swatch");
-  let onColorPickerReady = view.tooltips.colorPicker.once("ready");
+  let onColorPickerReady = view.tooltips.getTooltip("colorPicker").once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   let dropperButton = tooltip.doc.querySelector("#eyedropper-button");
 
   info("Click on the eyedropper icon");
   let onOpened = tooltip.once("eyedropper-opened");
   dropperButton.click();
--- a/devtools/client/inspector/rules/test/browser_rules_filtereditor-appears-on-swatch-click.js
+++ b/devtools/client/inspector/rules/test/browser_rules_filtereditor-appears-on-swatch-click.js
@@ -11,17 +11,17 @@ add_task(function* () {
   yield addTab(TEST_URL);
 
   let {view} = yield openRuleView();
 
   info("Getting the filter swatch element");
   let swatch = getRuleViewProperty(view, "body", "filter").valueSpan
     .querySelector(".ruleview-filterswatch");
 
-  let filterTooltip = view.tooltips.filterEditor;
+  let filterTooltip = view.tooltips.getTooltip("filterEditor");
   // Clicking on a cssfilter swatch sets the current filter value in the tooltip
   // which, in turn, makes the FilterWidget emit an "updated" event that causes
   // the rule-view to refresh. So we must wait for the ruleview-changed event.
   let onRuleViewChanged = view.once("ruleview-changed");
   swatch.click();
   yield onRuleViewChanged;
 
   ok(true, "The shown event was emitted after clicking on swatch");
--- a/devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js
+++ b/devtools/client/inspector/rules/test/browser_rules_filtereditor-commit-on-ENTER.js
@@ -19,17 +19,17 @@ add_task(function* () {
   // Clicking on a cssfilter swatch sets the current filter value in the tooltip
   // which, in turn, makes the FilterWidget emit an "updated" event that causes
   // the rule-view to refresh. So we must wait for the ruleview-changed event.
   let onRuleViewChanged = view.once("ruleview-changed");
   swatch.click();
   yield onRuleViewChanged;
 
   info("Get the cssfilter widget instance");
-  let filterTooltip = view.tooltips.filterEditor;
+  let filterTooltip = view.tooltips.getTooltip("filterEditor");
   let widget = filterTooltip.widget;
 
   info("Set a new value in the cssfilter widget");
   onRuleViewChanged = view.once("ruleview-changed");
   widget.setCssValue("blur(2px)");
   yield waitForComputedStyleProperty("body", null, "filter", "blur(2px)");
   yield onRuleViewChanged;
   ok(true, "Changes previewed on the element");
--- a/devtools/client/inspector/rules/test/browser_rules_filtereditor-revert-on-ESC.js
+++ b/devtools/client/inspector/rules/test/browser_rules_filtereditor-revert-on-ESC.js
@@ -97,22 +97,22 @@ function* clickOnFilterSwatch(swatch, vi
   let onRuleViewChanged = view.once("ruleview-changed");
   swatch.click();
   yield onRuleViewChanged;
 }
 
 function* setValueInFilterWidget(value, view) {
   info("Setting the CSS filter value in the tooltip");
 
-  let filterTooltip = view.tooltips.filterEditor;
+  let filterTooltip = view.tooltips.getTooltip("filterEditor");
   let onRuleViewChanged = view.once("ruleview-changed");
   filterTooltip.widget.setCssValue(value);
   yield onRuleViewChanged;
 }
 
 function* pressEscapeToCloseTooltip(view) {
   info("Pressing ESCAPE to close the tooltip");
 
-  let filterTooltip = view.tooltips.filterEditor;
+  let filterTooltip = view.tooltips.getTooltip("filterEditor");
   let onRuleViewChanged = view.once("ruleview-changed");
   EventUtils.sendKey("ESCAPE", filterTooltip.widget.styleWindow);
   yield onRuleViewChanged;
 }
--- a/devtools/client/inspector/rules/test/browser_rules_user-agent-styles-uneditable.js
+++ b/devtools/client/inspector/rules/test/browser_rules_user-agent-styles-uneditable.js
@@ -46,13 +46,13 @@ function* userAgentStylesUneditable(insp
       "nameSpan is not editable");
     ok(!firstProp.editor.valueSpan._editable,
       "valueSpan is not editable");
     ok(!rule.editor.closeBrace._editable, "closeBrace is not editable");
 
     let colorswatch = rule.editor.element
       .querySelector(".ruleview-colorswatch");
     if (colorswatch) {
-      ok(!view.tooltips.colorPicker.swatches.has(colorswatch),
+      ok(!view.tooltips.getTooltip("colorPicker").swatches.has(colorswatch),
         "The swatch is not editable");
     }
   }
 }
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -170,17 +170,17 @@ var simulateColorPickerChange = Task.asy
  *          - {String} value The expected style value
  * The style will be checked like so: getComputedStyle(element)[name] === value
  */
 var openColorPickerAndSelectColor = Task.async(function* (view, ruleIndex,
     propIndex, newRgba, expectedChange) {
   let ruleEditor = getRuleViewRuleEditor(view, ruleIndex);
   let propEditor = ruleEditor.rule.textProps[propIndex].editor;
   let swatch = propEditor.valueSpan.querySelector(".ruleview-colorswatch");
-  let cPicker = view.tooltips.colorPicker;
+  let cPicker = view.tooltips.getTooltip("colorPicker");
 
   info("Opening the colorpicker by clicking the color swatch");
   let onColorPickerReady = cPicker.once("ready");
   swatch.click();
   yield onColorPickerReady;
 
   yield simulateColorPickerChange(view, cPicker, newRgba, expectedChange);
 
@@ -208,17 +208,17 @@ var openColorPickerAndSelectColor = Task
  *          - {String} value The expected style value
  * The style will be checked like so: getComputedStyle(element)[name] === value
  */
 var openCubicBezierAndChangeCoords = Task.async(function* (view, ruleIndex,
     propIndex, coords, expectedChange) {
   let ruleEditor = getRuleViewRuleEditor(view, ruleIndex);
   let propEditor = ruleEditor.rule.textProps[propIndex].editor;
   let swatch = propEditor.valueSpan.querySelector(".ruleview-bezierswatch");
-  let bezierTooltip = view.tooltips.cubicBezier;
+  let bezierTooltip = view.tooltips.getTooltip("cubicBezier");
 
   info("Opening the cubicBezier by clicking the swatch");
   let onBezierWidgetReady = bezierTooltip.once("ready");
   swatch.click();
   yield onBezierWidgetReady;
 
   let widget = yield bezierTooltip.widget;
 
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -370,17 +370,17 @@ TextPropertyEditor.prototype = {
 
     // Attach the color picker tooltip to the color swatches
     this._colorSwatchSpans =
       this.valueSpan.querySelectorAll("." + COLOR_SWATCH_CLASS);
     if (this.ruleEditor.isEditable) {
       for (let span of this._colorSwatchSpans) {
         // Adding this swatch to the list of swatches our colorpicker
         // knows about
-        this.ruleView.tooltips.colorPicker.addSwatch(span, {
+        this.ruleView.tooltips.getTooltip("colorPicker").addSwatch(span, {
           onShow: this._onStartEditing,
           onPreview: this._onSwatchPreview,
           onCommit: this._onSwatchCommit,
           onRevert: this._onSwatchRevert
         });
         span.on("unit-change", this._onSwatchCommit);
         let title = l10n("rule.colorSwatch.tooltip");
         span.setAttribute("title", title);
@@ -390,34 +390,34 @@ TextPropertyEditor.prototype = {
 
     // Attach the cubic-bezier tooltip to the bezier swatches
     this._bezierSwatchSpans =
       this.valueSpan.querySelectorAll("." + BEZIER_SWATCH_CLASS);
     if (this.ruleEditor.isEditable) {
       for (let span of this._bezierSwatchSpans) {
         // Adding this swatch to the list of swatches our colorpicker
         // knows about
-        this.ruleView.tooltips.cubicBezier.addSwatch(span, {
+        this.ruleView.tooltips.getTooltip("cubicBezier").addSwatch(span, {
           onShow: this._onStartEditing,
           onPreview: this._onSwatchPreview,
           onCommit: this._onSwatchCommit,
           onRevert: this._onSwatchRevert
         });
         let title = l10n("rule.bezierSwatch.tooltip");
         span.setAttribute("title", title);
       }
     }
 
     // Attach the filter editor tooltip to the filter swatch
     let span = this.valueSpan.querySelector("." + FILTER_SWATCH_CLASS);
     if (this.ruleEditor.isEditable) {
       if (span) {
         parserOptions.filterSwatch = true;
 
-        this.ruleView.tooltips.filterEditor.addSwatch(span, {
+        this.ruleView.tooltips.getTooltip("filterEditor").addSwatch(span, {
           onShow: this._onStartEditing,
           onPreview: this._onSwatchPreview,
           onCommit: this._onSwatchCommit,
           onRevert: this._onSwatchRevert
         }, outputParser, parserOptions);
         let title = l10n("rule.filterSwatch.tooltip");
         span.setAttribute("title", title);
       }
@@ -737,17 +737,17 @@ TextPropertyEditor.prototype = {
    * direction.
    *
    * @param {Number} direction
    *        The move focus direction number.
    */
   remove: function (direction) {
     if (this._colorSwatchSpans && this._colorSwatchSpans.length) {
       for (let span of this._colorSwatchSpans) {
-        this.ruleView.tooltips.colorPicker.removeSwatch(span);
+        this.ruleView.tooltips.getTooltip("colorPicker").removeSwatch(span);
         span.off("unit-change", this._onSwatchCommit);
       }
     }
 
     if (this.angleSwatchSpans && this.angleSwatchSpans.length) {
       for (let span of this.angleSwatchSpans) {
         span.off("unit-change", this._onSwatchCommit);
       }
--- a/devtools/client/inspector/shared/style-inspector-menu.js
+++ b/devtools/client/inspector/shared/style-inspector-menu.js
@@ -409,17 +409,17 @@ StyleInspectorMenu.prototype = {
   }),
 
   /**
    *  Show docs from MDN for a CSS property.
    */
   _onShowMdnDocs: function () {
     let cssPropertyName = this.styleDocument.popupNode.textContent;
     let anchor = this.styleDocument.popupNode.parentNode;
-    let cssDocsTooltip = this.view.tooltips.cssDocs;
+    let cssDocsTooltip = this.view.tooltips.getTooltip("cssDocs");
     cssDocsTooltip.show(anchor, cssPropertyName);
   },
 
   /**
    * Add a new rule to the current element.
    */
   _onAddNewRule: function () {
     this.view._onAddRule();
--- a/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_02.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_02.js
@@ -75,17 +75,17 @@ function* testManualEdit(inspector, view
 function* testColorPickerEdit(inspector, view) {
   info("Testing colors edited via color picker");
   yield selectNode("div", inspector);
 
   let swatchElement = getRuleViewProperty(view, "div", "color").valueSpan
     .querySelector(".ruleview-colorswatch");
 
   info("Opening the color picker");
-  let picker = view.tooltips.colorPicker;
+  let picker = view.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = picker.once("ready");
   swatchElement.click();
   yield onColorPickerReady;
 
   let rgbaColor = [83, 183, 89, 1];
   let rgbaColorText = "rgba(83, 183, 89, 1)";
   yield simulateColorPickerChange(view, picker, rgbaColor);
 
--- a/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-background-image.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-background-image.js
@@ -47,79 +47,79 @@ add_task(function* () {
   yield onComputedViewReady;
 
   info("Testing that the background-image computed style has a tooltip too");
   yield testComputedView(view);
 });
 
 function* testBodyRuleView(view) {
   info("Testing tooltips in the rule view");
-  let panel = view.tooltips.previewTooltip.panel;
+  let panel = view.tooltips.getTooltip("previewTooltip").panel;
 
   // Check that the rule view has a tooltip and that a XUL panel has
   // been created
-  ok(view.tooltips.previewTooltip, "Tooltip instance exists");
+  ok(view.tooltips.getTooltip("previewTooltip"), "Tooltip instance exists");
   ok(panel, "XUL panel exists");
 
   // Get the background-image property inside the rule view
   let {valueSpan} = getRuleViewProperty(view, "body", "background-image");
   let uriSpan = valueSpan.querySelector(".theme-link");
 
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), uriSpan);
 
   let images = panel.getElementsByTagName("img");
   is(images.length, 1, "Tooltip contains an image");
   ok(images[0].getAttribute("src")
     .indexOf("iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHe") !== -1,
     "The image URL seems fine");
 }
 
 function* testDivRuleView(view) {
-  let panel = view.tooltips.previewTooltip.panel;
+  let panel = view.tooltips.getTooltip("previewTooltip").panel;
 
   // Get the background property inside the rule view
   let {valueSpan} = getRuleViewProperty(view, ".test-element", "background");
   let uriSpan = valueSpan.querySelector(".theme-link");
 
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), uriSpan);
 
   let images = panel.getElementsByTagName("img");
   is(images.length, 1, "Tooltip contains an image");
   ok(images[0].getAttribute("src").startsWith("data:"),
     "Tooltip contains a data-uri image as expected");
 }
 
 function* testTooltipAppearsEvenInEditMode(view) {
   info("Switching to edit mode in the rule view");
   let editor = yield turnToEditMode(view);
 
   info("Now trying to show the preview tooltip");
   let {valueSpan} = getRuleViewProperty(view, ".test-element", "background");
   let uriSpan = valueSpan.querySelector(".theme-link");
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), uriSpan);
 
   is(view.styleDocument.activeElement, editor.input,
     "Tooltip was shown in edit mode, and inplace-editor still focused");
 }
 
 function turnToEditMode(ruleView) {
   let brace = ruleView.styleDocument.querySelector(".ruleview-ruleclose");
   return focusEditableField(ruleView, brace);
 }
 
 function* testComputedView(view) {
-  let tooltip = view.tooltips.previewTooltip;
+  let tooltip = view.tooltips.getTooltip("previewTooltip");
   ok(tooltip, "The computed-view has a tooltip defined");
 
   let panel = tooltip.panel;
   ok(panel, "The computed-view tooltip has a XUL panel");
 
   let {valueSpan} = getComputedViewProperty(view, "background-image");
   let uriSpan = valueSpan.querySelector(".theme-link");
 
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), uriSpan);
 
   let images = panel.getElementsByTagName("img");
   is(images.length, 1, "Tooltip contains an image");
 
   ok(images[0].getAttribute("src").startsWith("data:"),
     "Tooltip contains a data-uri in the computed-view too");
 }
--- a/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-closes-on-new-selection.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-closes-on-new-selection.js
@@ -20,17 +20,17 @@ add_task(function* () {
   info("Testing computed view tooltip closes on new selection");
   view = selectComputedView(inspector);
   yield testComputedView(view, inspector);
 });
 
 function* testRuleView(ruleView, inspector) {
   info("Showing the tooltip");
 
-  let tooltip = ruleView.tooltips.previewTooltip;
+  let tooltip = ruleView.tooltips.getTooltip("previewTooltip");
   let tooltipContent = ruleView.styleDocument.createElementNS(XHTML_NS, "div");
   yield tooltip.setContent(tooltipContent, {width: 100, height: 30});
 
   // Stop listening for mouse movements because it's not needed for this test,
   // and causes intermittent failures on Linux. When this test runs in the suite
   // sometimes a mouseleave event is dispatched at the start, which causes the
   // tooltip to hide in the middle of being shown, which causes timeouts later.
   tooltip.stopTogglingOnHover();
@@ -45,17 +45,17 @@ function* testRuleView(ruleView, inspect
   yield onHidden;
 
   ok(true, "Rule view tooltip closed after a new node got selected");
 }
 
 function* testComputedView(computedView, inspector) {
   info("Showing the tooltip");
 
-  let tooltip = computedView.tooltips.previewTooltip;
+  let tooltip = computedView.tooltips.getTooltip("previewTooltip");
   let tooltipContent = computedView.styleDocument.createElementNS(XHTML_NS, "div");
   yield tooltip.setContent(tooltipContent, {width: 100, height: 30});
 
   // Stop listening for mouse movements because it's not needed for this test,
   // and causes intermittent failures on Linux. When this test runs in the suite
   // sometimes a mouseleave event is dispatched at the start, which causes the
   // tooltip to hide in the middle of being shown, which causes timeouts later.
   tooltip.stopTogglingOnHover();
--- a/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-longhand-fontfamily.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-longhand-fontfamily.js
@@ -31,17 +31,17 @@ add_task(function* () {
   yield testComputedView(view, inspector.selection.nodeFront);
 
   yield testExpandedComputedViewProperty(view, inspector.selection.nodeFront);
 });
 
 function* testRuleView(ruleView, nodeFront) {
   info("Testing font-family tooltips in the rule view");
 
-  let tooltip = ruleView.tooltips.previewTooltip;
+  let tooltip = ruleView.tooltips.getTooltip("previewTooltip");
   let panel = tooltip.panel;
 
   // Check that the rule view has a tooltip and that a XUL panel has
   // been created
   ok(tooltip, "Tooltip instance exists");
   ok(panel, "XUL panel exists");
 
   // Get the font family property inside the rule view
@@ -59,17 +59,17 @@ function* testRuleView(ruleView, nodeFro
   let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
   is(images[0].getAttribute("src"), dataURL,
     "Tooltip contains the correct data-uri image");
 }
 
 function* testComputedView(computedView, nodeFront) {
   info("Testing font-family tooltips in the computed view");
 
-  let tooltip = computedView.tooltips.previewTooltip;
+  let tooltip = computedView.tooltips.getTooltip("previewTooltip");
   let panel = tooltip.panel;
   let {valueSpan} = getComputedViewProperty(computedView, "font-family");
 
   yield assertHoverTooltipOn(tooltip, valueSpan);
 
   let images = panel.getElementsByTagName("img");
   is(images.length, 1, "Tooltip contains an image");
   ok(images[0].getAttribute("src").startsWith("data:"),
@@ -87,17 +87,17 @@ function* testExpandedComputedViewProper
   info("Expanding the font-family property to reveal matched selectors");
   let propertyView = getPropertyView(computedView, "font-family");
   propertyView.matchedExpanded = true;
   yield propertyView.refreshMatchedSelectors();
 
   let valueSpan = propertyView.matchedSelectorsContainer
     .querySelector(".bestmatch .other-property-value");
 
-  let tooltip = computedView.tooltips.previewTooltip;
+  let tooltip = computedView.tooltips.getTooltip("previewTooltip");
   let panel = tooltip.panel;
 
   yield assertHoverTooltipOn(tooltip, valueSpan);
 
   let images = panel.getElementsByTagName("img");
   is(images.length, 1, "Tooltip contains an image");
   ok(images[0].getAttribute("src").startsWith("data:"),
     "Tooltip contains a data-uri image as expected");
--- a/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-multiple-background-images.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-multiple-background-images.js
@@ -46,18 +46,18 @@ function* testComputedViewUrls(inspector
 function* performChecks(view, propertyValue) {
   function checkTooltip(panel, imageSrc) {
     let images = panel.getElementsByTagName("img");
     is(images.length, 1, "Tooltip contains an image");
     is(images[0].getAttribute("src"), imageSrc, "The image URL is correct");
   }
 
   let links = propertyValue.querySelectorAll(".theme-link");
-  let panel = view.tooltips.previewTooltip.panel;
+  let panel = view.tooltips.getTooltip("previewTooltip").panel;
 
   info("Checking first link tooltip");
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, links[0]);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), links[0]);
   checkTooltip(panel, YELLOW_DOT);
 
   info("Checking second link tooltip");
-  yield assertHoverTooltipOn(view.tooltips.previewTooltip, links[1]);
+  yield assertHoverTooltipOn(view.tooltips.getTooltip("previewTooltip"), links[1]);
   checkTooltip(panel, BLUE_DOT);
 }
--- a/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-shorthand-fontfamily.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-shorthand-fontfamily.js
@@ -21,17 +21,17 @@ add_task(function* () {
 
   yield selectNode("#testElement", inspector);
   yield testRuleView(view, inspector.selection.nodeFront);
 });
 
 function* testRuleView(ruleView, nodeFront) {
   info("Testing font-family tooltips in the rule view");
 
-  let tooltip = ruleView.tooltips.previewTooltip;
+  let tooltip = ruleView.tooltips.getTooltip("previewTooltip");
   let panel = tooltip.panel;
 
   // Check that the rule view has a tooltip and that a XUL panel has
   // been created
   ok(tooltip, "Tooltip instance exists");
   ok(panel, "XUL panel exists");
 
   // Get the computed font family property inside the font rule view
--- a/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-size.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_tooltip-size.js
@@ -23,17 +23,17 @@ add_task(function* () {
   yield selectNode("div", inspector);
   yield testImageDimension(view);
   yield testPickerDimension(view);
 });
 
 function* testImageDimension(ruleView) {
   info("Testing background-image tooltip dimensions");
 
-  let tooltip = ruleView.tooltips.previewTooltip;
+  let tooltip = ruleView.tooltips.getTooltip("previewTooltip");
   let panel = tooltip.panel;
   let {valueSpan} = getRuleViewProperty(ruleView, "div", "background");
   let uriSpan = valueSpan.querySelector(".theme-link");
 
   // Make sure there is a hover tooltip for this property, this also will fill
   // the tooltip with its content
   yield assertHoverTooltipOn(tooltip, uriSpan);
 
@@ -57,17 +57,17 @@ function* testImageDimension(ruleView) {
   yield onHidden;
 }
 
 function* testPickerDimension(ruleView) {
   info("Testing color-picker tooltip dimensions");
 
   let {valueSpan} = getRuleViewProperty(ruleView, "div", "background");
   let swatch = valueSpan.querySelector(".ruleview-colorswatch");
-  let cPicker = ruleView.tooltips.colorPicker;
+  let cPicker = ruleView.tooltips.getTooltip("colorPicker");
 
   let onReady = cPicker.once("ready");
   swatch.click();
   yield onReady;
 
   // The colorpicker spectrum's iframe has a fixed width height, so let's
   // make sure the tooltip is at least as big as that
   let spectrumRect = cPicker.spectrum.element.getBoundingClientRect();
--- a/devtools/client/inspector/shared/tooltips-overlay.js
+++ b/devtools/client/inspector/shared/tooltips-overlay.js
@@ -13,125 +13,147 @@
 
 const { Task } = require("devtools/shared/task");
 const Services = require("Services");
 const {
   VIEW_NODE_VALUE_TYPE,
   VIEW_NODE_IMAGE_URL_TYPE,
 } = require("devtools/client/inspector/shared/node-types");
 const { getColor } = require("devtools/client/shared/theme");
-const { getCssProperties } = require("devtools/shared/fronts/css-properties");
-const CssDocsTooltip = require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
 const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
-const {
-  getImageDimensions,
-  setImageTooltip,
-  setBrokenImageTooltip,
-} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
-const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
-const SwatchCubicBezierTooltip = require("devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip");
-const SwatchFilterTooltip = require("devtools/client/shared/widgets/tooltip/SwatchFilterTooltip");
+
+loader.lazyRequireGetter(this, "getCssProperties",
+  "devtools/shared/fronts/css-properties", true);
+
+loader.lazyRequireGetter(this, "getImageDimensions",
+  "devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
+loader.lazyRequireGetter(this, "setImageTooltip",
+  "devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
+loader.lazyRequireGetter(this, "setBrokenImageTooltip",
+  "devtools/client/shared/widgets/tooltip/ImageTooltipHelper", true);
 
 const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
 
 // Types of existing tooltips
 const TOOLTIP_IMAGE_TYPE = "image";
 const TOOLTIP_FONTFAMILY_TYPE = "font-family";
 
 /**
  * Manages all tooltips in the style-inspector.
  *
  * @param {CssRuleView|CssComputedView} view
  *        Either the rule-view or computed-view panel
  */
 function TooltipsOverlay(view) {
   this.view = view;
-
-  let {CssRuleView} = require("devtools/client/inspector/rules/rules");
-  this.isRuleView = view instanceof CssRuleView;
-  this._cssProperties = getCssProperties(this.view.inspector.toolbox);
+  this._instances = new Map();
 
   this._onNewSelection = this._onNewSelection.bind(this);
   this.view.inspector.selection.on("new-node-front", this._onNewSelection);
+
+  this.addToView();
 }
 
 TooltipsOverlay.prototype = {
+  get _cssProperties() {
+    delete TooltipsOverlay.prototype._cssProperties;
+    let properties = getCssProperties(this.view.inspector.toolbox);
+    TooltipsOverlay.prototype._cssProperties = properties;
+    return properties;
+  },
+
   get isEditing() {
-    return this.colorPicker.tooltip.isVisible() ||
-           this.colorPicker.eyedropperOpen ||
-           this.cubicBezier.tooltip.isVisible() ||
-           this.filterEditor.tooltip.isVisible();
+    for (let [, tooltip] of this._instances) {
+      if (typeof (tooltip.isEditing) == "function" && tooltip.isEditing()) {
+        return true;
+      }
+    }
+    return false;
   },
 
   /**
    * Add the tooltips overlay to the view. This will start tracking mouse
    * movements and display tooltips when needed
    */
   addToView: function () {
     if (this._isStarted || this._isDestroyed) {
       return;
     }
 
-    let { toolbox } = this.view.inspector;
+    this._isStarted = true;
 
-    // Initializing the different tooltips that are used in the inspector.
-    // These tooltips are attached to the toolbox document if they require a popup panel.
-    // Otherwise, it is attached to the inspector panel document if it is an inline
-    // editor.
-    this.previewTooltip = new HTMLTooltip(toolbox.doc, {
-      type: "arrow",
-      useXulWrapper: true
-    });
-    this.previewTooltip.startTogglingOnHover(this.view.element,
-      this._onPreviewTooltipTargetHover.bind(this));
+    // For now, preview tooltip has to be instanciated on startup in order to
+    // call tooltip.startTogglingOnHover. Ideally startTogglingOnHover wouldn't be part
+    // of HTMLTooltip and offer a way to lazy load this tooltip.
+    this.getTooltip("previewTooltip");
+  },
 
-    // MDN CSS help tooltip
-    this.cssDocs = new CssDocsTooltip(toolbox.doc);
-
-    if (this.isRuleView) {
-      // Color picker tooltip
-      this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc,
-                                                      this.view.inspector,
-                                                      this._cssProperties);
-      // Cubic bezier tooltip
-      this.cubicBezier = new SwatchCubicBezierTooltip(toolbox.doc);
-      // Filter editor tooltip
-      this.filterEditor = new SwatchFilterTooltip(toolbox.doc,
-        this._cssProperties.getValidityChecker(this.view.inspector.panelDoc));
+  /**
+   * Lazily fetch and initialize the different tooltips that are used in the inspector.
+   * These tooltips are attached to the toolbox document if they require a popup panel.
+   * Otherwise, it is attached to the inspector panel document if it is an inline editor.
+   *
+   * @param {String} name
+   *        Identifier name for the tooltip
+   */
+  getTooltip: function (name) {
+    let tooltip = this._instances.get(name);
+    if (tooltip) {
+      return tooltip;
     }
-
-    this._isStarted = true;
+    let { doc } = this.view.inspector.toolbox;
+    switch (name) {
+      case "colorPicker":
+        const SwatchColorPickerTooltip =
+          require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
+        tooltip = new SwatchColorPickerTooltip(doc, this.view.inspector,
+          this._cssProperties);
+        break;
+      case "cubicBezier":
+        const SwatchCubicBezierTooltip =
+          require("devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip");
+        tooltip = new SwatchCubicBezierTooltip(doc);
+        break;
+      case "filterEditor":
+        const SwatchFilterTooltip =
+          require("devtools/client/shared/widgets/tooltip/SwatchFilterTooltip");
+        tooltip = new SwatchFilterTooltip(doc,
+          this._cssProperties.getValidityChecker(this.view.inspector.panelDoc));
+        break;
+      case "cssDocs":
+        const CssDocsTooltip =
+          require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
+        tooltip = new CssDocsTooltip(doc);
+        break;
+      case "previewTooltip":
+        tooltip = new HTMLTooltip(doc, {
+          type: "arrow",
+          useXulWrapper: true
+        });
+        tooltip.startTogglingOnHover(this.view.element,
+          this._onPreviewTooltipTargetHover.bind(this));
+        break;
+      default:
+        throw new Error(`Unsupported tooltip '${name}'`);
+    }
+    this._instances.set(name, tooltip);
+    return tooltip;
   },
 
   /**
    * Remove the tooltips overlay from the view. This will stop tracking mouse
    * movements and displaying tooltips
    */
   removeFromView: function () {
     if (!this._isStarted || this._isDestroyed) {
       return;
     }
 
-    this.previewTooltip.stopTogglingOnHover(this.view.element);
-    this.previewTooltip.destroy();
-
-    if (this.colorPicker) {
-      this.colorPicker.destroy();
-    }
-
-    if (this.cubicBezier) {
-      this.cubicBezier.destroy();
-    }
-
-    if (this.cssDocs) {
-      this.cssDocs.destroy();
-    }
-
-    if (this.filterEditor) {
-      this.filterEditor.destroy();
+    for (let [, tooltip] of this._instances) {
+      tooltip.destroy();
     }
 
     this._isStarted = false;
   },
 
   /**
    * Given a hovered node info, find out which type of tooltip should be shown,
    * if any
@@ -177,42 +199,31 @@ TooltipsOverlay.prototype = {
     }
 
     let type = this._getTooltipType(nodeInfo);
     if (!type) {
       // There is no tooltip type defined for the hovered node
       return false;
     }
 
-    if (this.isRuleView && this.colorPicker.tooltip.isVisible()) {
-      this.colorPicker.revert();
-      this.colorPicker.hide();
-    }
-
-    if (this.isRuleView && this.cubicBezier.tooltip.isVisible()) {
-      this.cubicBezier.revert();
-      this.cubicBezier.hide();
-    }
-
-    if (this.isRuleView && this.cssDocs.tooltip.isVisible()) {
-      this.cssDocs.hide();
-    }
-
-    if (this.isRuleView && this.filterEditor.tooltip.isVisible()) {
-      this.filterEditor.revert();
-      this.filterEdtior.hide();
+    for (let [, tooltip] of this._instances) {
+      if (tooltip.isVisible()) {
+        tooltip.revert();
+        tooltip.hide();
+      }
     }
 
     let inspector = this.view.inspector;
 
     if (type === TOOLTIP_IMAGE_TYPE) {
       try {
         yield this._setImagePreviewTooltip(nodeInfo.value.url);
       } catch (e) {
-        yield setBrokenImageTooltip(this.previewTooltip, this.view.inspector.panelDoc);
+        yield setBrokenImageTooltip(this.getTooltip("previewTooltip"),
+          this.view.inspector.panelDoc);
       }
       return true;
     }
 
     if (type === TOOLTIP_FONTFAMILY_TYPE) {
       let font = nodeInfo.value.value;
       let nodeFront = inspector.selection.nodeFront;
       yield this._setFontPreviewTooltip(font, nodeFront);
@@ -244,17 +255,17 @@ TooltipsOverlay.prototype = {
     } else {
       let inspectorFront = this.view.inspector.inspector;
       let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl, maxDim);
       imageUrl = yield data.string();
       naturalWidth = size.naturalWidth;
       naturalHeight = size.naturalHeight;
     }
 
-    yield setImageTooltip(this.previewTooltip, doc, imageUrl,
+    yield setImageTooltip(this.getTooltip("previewTooltip"), doc, imageUrl,
       {maxDim, naturalWidth, naturalHeight});
   }),
 
   /**
    * Set the content of the preview tooltip to display a font family preview.
    *
    * @param {String} font
    *        The font family value.
@@ -274,40 +285,24 @@ TooltipsOverlay.prototype = {
 
     let fillStyle = getColor("body-color");
     let {data, size: maxDim} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
 
     let imageUrl = yield data.string();
     let doc = this.view.inspector.panelDoc;
     let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);
 
-    yield setImageTooltip(this.previewTooltip, doc, imageUrl,
+    yield setImageTooltip(this.getTooltip("previewTooltip"), doc, imageUrl,
       {hideDimensionLabel: true, hideCheckeredBackground: true,
        maxDim, naturalWidth, naturalHeight});
   }),
 
   _onNewSelection: function () {
-    if (this.previewTooltip) {
-      this.previewTooltip.hide();
-    }
-
-    if (this.colorPicker) {
-      this.colorPicker.hide();
-    }
-
-    if (this.cubicBezier) {
-      this.cubicBezier.hide();
-    }
-
-    if (this.cssDocs) {
-      this.cssDocs.hide();
-    }
-
-    if (this.filterEditor) {
-      this.filterEditor.hide();
+    for (let [, tooltip] of this._instances) {
+      tooltip.hide();
     }
   },
 
   /**
    * Destroy this overlay instance, removing it from the view
    */
   destroy: function () {
     this.removeFromView();
--- a/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
@@ -21,17 +21,17 @@ add_task(function* () {
   yield selectNode("#scale", inspector);
 
   // Find the color swatch in the rule-view.
   let ruleView = inspector.getPanel("ruleview").view;
   let ruleViewDocument = ruleView.styleDocument;
   let swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
 
   info("Open the color picker");
-  let cPicker = ruleView.tooltips.colorPicker;
+  let cPicker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = cPicker.once("ready");
   swatchEl.click();
   yield onColorPickerReady;
 
   button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
   ok(isDisabled(button), "The button is disabled in the color picker");
 
   info("Navigate to a HTML document");
@@ -45,17 +45,17 @@ add_task(function* () {
   // Find the color swatch in the rule-view.
   yield selectNode("h1", inspector);
 
   ruleView = inspector.getPanel("ruleview").view;
   ruleViewDocument = ruleView.styleDocument;
   swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
 
   info("Open the color picker in HTML document");
-  cPicker = ruleView.tooltips.colorPicker;
+  cPicker = ruleView.tooltips.getTooltip("colorPicker");
   onColorPickerReady = cPicker.once("ready");
   swatchEl.click();
   yield onColorPickerReady;
 
   button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
   ok(!isDisabled(button), "The button is enabled in the color picker");
 });
 
--- a/devtools/client/shared/widgets/tooltip/CssDocsTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/CssDocsTooltip.js
@@ -34,30 +34,41 @@ function CssDocsTooltip(toolboxDoc) {
   this.shortcuts = new KeyShortcuts({ window: this.tooltip.topWindow });
   this._onShortcut = this._onShortcut.bind(this);
 
   this.shortcuts.on("Escape", this._onShortcut);
 }
 
 CssDocsTooltip.prototype = {
   /**
+   * Reports if the tooltip is currently shown
+   *
+   * @return {Boolean} True if the tooltip is displayed.
+   */
+  isVisible: function () {
+    return this.tooltip.isVisible();
+  },
+
+  /**
    * Load CSS docs for the given property,
    * then display the tooltip.
    */
   show: function (anchor, propertyName) {
     this.tooltip.once("shown", () => {
       this.widget.loadCssDocs(propertyName);
     });
     this.tooltip.show(anchor);
   },
 
   hide: function () {
     this.tooltip.hide();
   },
 
+  revert: function () {},
+
   _onShortcut: function (shortcut, event) {
     if (!this.tooltip.isVisible()) {
       return;
     }
     event.stopPropagation();
     event.preventDefault();
     this.hide();
   },
--- a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
@@ -478,16 +478,17 @@ HTMLTooltip.prototype = {
    * tooltip container from the document.
    */
   destroy: function () {
     this.hide();
     this.container.remove();
     if (this.xulPanelWrapper) {
       this.xulPanelWrapper.remove();
     }
+    this._toggle.destroy();
   },
 
   _createContainer: function () {
     let container = this.doc.createElementNS(XHTML_NS, "div");
     container.setAttribute("type", this.type);
     container.classList.add("tooltip-container");
 
     let html = '<div class="tooltip-filler"></div>';
--- a/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
@@ -76,16 +76,34 @@ function SwatchBasedEditorTooltip(docume
   // that was clicked
   this.activeSwatch = null;
 
   this._onSwatchClick = this._onSwatchClick.bind(this);
 }
 
 SwatchBasedEditorTooltip.prototype = {
   /**
+   * Reports if the tooltip is currently shown
+   *
+   * @return {Boolean} True if the tooltip is displayed.
+   */
+  isVisible: function () {
+    return this.tooltip.isVisible();
+  },
+
+  /**
+   * Reports if the tooltip is currently editing the targeted value
+   *
+   * @return {Boolean} True if the tooltip is editing.
+   */
+  isEditing: function () {
+    return this.isVisible();
+  },
+
+  /**
    * Show the editor tooltip for the currently active swatch.
    *
    * @return {Promise} a promise that resolves once the editor tooltip is displayed, or
    *         immediately if there is no currently active swatch.
    */
   show: function () {
     let tooltipAnchor = this.useInline ?
       this.activeSwatch.closest(`.${INLINE_TOOLTIP_CLASS}`) :
--- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -199,16 +199,24 @@ SwatchColorPickerTooltip.prototype = Her
   },
 
   _toDefaultType: function (color) {
     let colorObj = new colorUtils.CssColor(color);
     colorObj.setAuthoredUnitFromColor(this._originalColor, this.cssColor4);
     return colorObj.toString();
   },
 
+  /**
+   * Overriding the SwatchBasedEditorTooltip.isEditing function to consider the
+   * eyedropper.
+   */
+  isEditing: function () {
+    return this.tooltip.isVisible() || this.eyedropperOpen;
+  },
+
   destroy: function () {
     SwatchBasedEditorTooltip.prototype.destroy.call(this);
     this.inspector = null;
     this.currentSwatchColor = null;
     this.spectrum.off("changed", this._onSpectrumColorChange);
     this.spectrum.destroy();
   }
 });