Bug 1476311 - Part 2: Remove form elements from innerHTML. r?gl draft
authorMicah Tigley <mtigley@mozilla.com>
Tue, 17 Jul 2018 22:09:08 -0400
changeset 819532 189e9244b601889213392f011356c0ef70223d02
parent 819531 03124f7cb78f354659d6a8fdd1506b9baeb7e686
push id116575
push userbmo:mtigley@mozilla.com
push dateWed, 18 Jul 2018 02:33:37 +0000
reviewersgl
bugs1476311
milestone63.0a1
Bug 1476311 - Part 2: Remove form elements from innerHTML. r?gl MozReview-Commit-ID: LQVwyWJ9OIW
devtools/client/shared/widgets/ColorWidget.js
--- a/devtools/client/shared/widgets/ColorWidget.js
+++ b/devtools/client/shared/widgets/ColorWidget.js
@@ -256,16 +256,132 @@ ColorWidget.prototype = {
     return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
   },
 
   get rgbCssString() {
     const rgb = this.rgb;
     return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")";
   },
 
+  buildContrastHelpButton(doc) {
+    this.contrastHelp = doc.createElementNS(XHTML_NS, "button");
+    this.contrastHelp.className = "colorwidget-contrast-help";
+    this.contrastHelp.classList.add("devtools-button");
+
+    const contrastInner = this.element.querySelector(".colorwidget-contrast-inner");
+    contrastInner.appendChild(this.contrastHelp);
+  },
+
+  buildColorSelect(doc) {
+    // Create markup for color widget's select component
+    this.colorWidgetValue = this.element.querySelector(".colorwidget-value");
+    this.colorSelect = doc.createElementNS(XHTML_NS, "select");
+    this.colorSelect.id = "colorwidget-select";
+    const colorWidgetHex = this.element.querySelector(".colorwidget-hex");
+    this.colorWidgetValue.insertBefore(this.colorSelect, colorWidgetHex);
+
+    // Create hex option
+    this.hexOption = doc.createElementNS(XHTML_NS, "option");
+    this.hexOption.value = "hex";
+    this.hexOption.appendChild(doc.createTextNode("Hex"));
+
+    // Create RGBA option
+    this.rgbaOption = doc.createElementNS(XHTML_NS, "option");
+    this.rgbaOption.value = "rgba";
+    this.rgbaOption.appendChild(doc.createTextNode("RGBA"));
+
+    // Create HSLA option
+    this.hslaOption = doc.createElementNS(XHTML_NS, "option");
+    this.hslaOption.value = "hsla";
+    this.hslaOption.appendChild(doc.createTextNode("HSLA"));
+
+    // Append options to the colorSelect element.
+    this.colorSelect.appendChild(this.hexOption);
+    this.colorSelect.appendChild(this.rgbaOption);
+    this.colorSelect.appendChild(this.hslaOption);
+
+    this.colorSelect.addEventListener("change", this.onSelectValueChange);
+  },
+
+  buildHexInput(doc) {
+    // Create markup for color widget's hex input component
+    this.hexValue = this.element.querySelector(".colorwidget-hex");
+    this.hexValueInput = doc.createElementNS(XHTML_NS, "input");
+    this.hexValueInput.className = "colorwidget-hex-input";
+    this.hexValue.appendChild(this.hexValueInput);
+
+    this.hexValueInput.addEventListener("input", this.onHexInputChange);
+  },
+
+  buildRGBAInput(doc) {
+   // Create markup for color widget's rgba's inputs
+    this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
+    this.rgbaR = doc.createElementNS(XHTML_NS, "input");
+    this.rgbaR.className = "colorwidget-rgba-r";
+    this.rgbaR["data-id"] = "r";
+
+    this.rgbaG = doc.createElementNS(XHTML_NS, "input");
+    this.rgbaG.className = "colorwidget-rgba-g";
+    this.rgbaG["data-id"] = "g";
+
+    this.rgbaB = doc.createElementNS(XHTML_NS, "input");
+    this.rgbaB.className = "colorwidget-rgba-b";
+    this.rgbaB["data-id"] = "b";
+
+    this.rgbaA = doc.createElementNS(XHTML_NS, "input");
+    this.rgbaA.className = "colorwidget-rgba-a";
+    this.rgbaA["data-id"] = "a";
+
+    this.rgbaValue.appendChild(this.rgbaR);
+    this.rgbaValue.appendChild(this.rgbaG);
+    this.rgbaValue.appendChild(this.rgbaB);
+    this.rgbaValue.appendChild(this.rgbaA);
+
+    this.rgbaValueInputs = {
+      r: this.rgbaR,
+      g: this.rgbaG,
+      b: this.rgbaB,
+      a: this.rgbaA,
+    };
+    this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
+  },
+
+  buildHSLAInput(doc) {
+    // Create markup for color widget's hsla's inputs
+    this.hslaValue = this.element.querySelector(".colorwidget-hsla");
+    this.hslaH = doc.createElementNS(XHTML_NS, "input");
+    this.hslaH.className = "colorwidget-hsla-h";
+    this.hslaH["data-id"] = "h";
+
+    this.hslaS = doc.createElementNS(XHTML_NS, "input");
+    this.hslaS.className = "colorwidget-hsla-s";
+    this.hslaS["data-id"] = "s";
+
+    this.hslaL = doc.createElementNS(XHTML_NS, "input");
+    this.hslaL.className = "colorwidget-hsla-l";
+    this.hslaL["data-id"] = "l";
+
+    this.hslaA = doc.createElementNS(XHTML_NS, "input");
+    this.hslaA.className = "colorwidget-hsla-a";
+    this.hslaA["data-id"] = "a";
+
+    this.hslaValue.appendChild(this.hslaH);
+    this.hslaValue.appendChild(this.hslaS);
+    this.hslaValue.appendChild(this.hslaL);
+    this.hslaValue.appendChild(this.hslaA);
+
+    this.hslaValueInputs = {
+      h: this.hslaH,
+      s: this.hslaS,
+      l: this.hslaL,
+      a: this.hslaA,
+    };
+    this.hslaValue.addEventListener("input", this.onHslaInputChange);
+  },
+
   initializeColorWidget: function() {
     const colorNameLabel = L10N.getStr("inspector.colorwidget.colorNameLabel");
     this.parentEl.innerHTML = "";
     this.element = this.parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
 
     this.element.className = "colorwidget-container";
     // eslint-disable-next-line no-unsanitized/property
     this.element.innerHTML = `
@@ -285,21 +401,16 @@ ColorWidget.prototype = {
         </div>
       </div>
       <div class="colorwidget-alpha colorwidget-checker colorwidget-box">
         <div class="colorwidget-alpha-inner">
           <div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
         </div>
       </div>
       <div class="colorwidget-value">
-        <select class="colorwidget-select">
-          <option value="hex">Hex</option>
-          <option value="rgba">RGBA</option>
-          <option value="hsla">HSLA</option>
-        </select>
         <div class="colorwidget-hex">
           <input class="colorwidget-hex-input"/>
         </div>
         <div class="colorwidget-rgba colorwidget-hidden">
           <input class="colorwidget-rgba-r" data-id="r" />
           <input class="colorwidget-rgba-g" data-id="g" />
           <input class="colorwidget-rgba-b" data-id="b" />
           <input class="colorwidget-rgba-a" data-id="a" />
@@ -316,21 +427,31 @@ ColorWidget.prototype = {
         <span class="colorwidget-colorname-value"></span>
       </div>
     <div class="colorwidget-contrast">
       <div class="colorwidget-contrast-info"></div>
       <div class="colorwidget-contrast-inner">
         <span class="colorwidget-colorswatch"></span>
         <span class="colorwidget-contrast-ratio"></span>
         <span class="colorwidget-contrast-grade"></span>
-        <button class="colorwidget-contrast-help devtools-button"></button>
       </div>
     </div>
     `;
 
+    const doc = this.element.ownerDocument;
+
+    // Form elements need to be appended manually after innerHTML is declared.
+    // This must be done due to security issues of having form elements available
+    // in innerHTML.
+    this.buildColorSelect(doc);
+    this.buildHexInput(doc);
+    this.buildRGBAInput(doc);
+    this.buildHSLAInput(doc);
+    this.buildContrastHelpButton(doc);
+
     this.element.addEventListener("click", this.onElementClick);
 
     this.parentEl.appendChild(this.element);
 
     this.closestBackgroundColor = "rgba(255, 255, 255, 1)";
 
     this.colorName = this.element.querySelector(".colorwidget-colorname-value");
 
@@ -356,41 +477,16 @@ ColorWidget.prototype = {
     this.dragger = this.element.querySelector(".colorwidget-color");
     this.dragHelper = this.element.querySelector(".colorwidget-dragger");
     ColorWidget.draggable(this.dragger, this.onDraggerMove);
 
     this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
     this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
     this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
     ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove);
-
-    this.colorSelect = this.element.querySelector(".colorwidget-select");
-    this.colorSelect.addEventListener("change", this.onSelectValueChange);
-
-    this.hexValue = this.element.querySelector(".colorwidget-hex");
-    this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
-    this.hexValueInput.addEventListener("input", this.onHexInputChange);
-
-    this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
-    this.rgbaValueInputs = {
-      r: this.element.querySelector(".colorwidget-rgba-r"),
-      g: this.element.querySelector(".colorwidget-rgba-g"),
-      b: this.element.querySelector(".colorwidget-rgba-b"),
-      a: this.element.querySelector(".colorwidget-rgba-a"),
-    };
-    this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
-
-    this.hslaValue = this.element.querySelector(".colorwidget-hsla");
-    this.hslaValueInputs = {
-      h: this.element.querySelector(".colorwidget-hsla-h"),
-      s: this.element.querySelector(".colorwidget-hsla-s"),
-      l: this.element.querySelector(".colorwidget-hsla-l"),
-      a: this.element.querySelector(".colorwidget-hsla-a"),
-    };
-    this.hslaValue.addEventListener("input", this.onHslaInputChange);
   },
 
   async show() {
     this.initializeColorWidget();
     this.element.classList.add("colorwidget-show");
 
     this.slideHeight = this.slider.offsetHeight;
     this.dragWidth = this.dragger.offsetWidth;