| Index: third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js
 | 
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js b/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js
 | 
| index 74a23375f05e4e34776ea655363b76441b91a928..646927096b44fd4a166b64c0feaf6a20c511646c 100644
 | 
| --- a/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js
 | 
| +++ b/third_party/WebKit/Source/devtools/front_end/ui/CSSShadowEditor.js
 | 
| @@ -21,20 +21,27 @@ WebInspector.CSSShadowEditor = function()
 | 
|      this._insetButton.textContent = WebInspector.UIString("Inset");
 | 
|      this._insetButton.addEventListener("click", this._onButtonClick.bind(this), false);
 | 
|  
 | 
| -    var inputs;
 | 
| -    inputs = this._createSliderField(WebInspector.UIString("X offset"), true);
 | 
| -    this._xInput = inputs.textInput;
 | 
| -    this._xSlider = inputs.rangeInput;
 | 
| -    inputs = this._createSliderField(WebInspector.UIString("Y offset"), true);
 | 
| -    this._yInput = inputs.textInput;
 | 
| -    this._ySlider = inputs.rangeInput;
 | 
| -    inputs = this._createSliderField(WebInspector.UIString("Blur"), false);
 | 
| -    this._blurInput = inputs.textInput;
 | 
| -    this._blurSlider = inputs.rangeInput;
 | 
| -    inputs = this._createSliderField(WebInspector.UIString("Spread"), false);
 | 
| -    this._spreadInput = inputs.textInput;
 | 
| -    this._spreadSlider = inputs.rangeInput;
 | 
| -    this._spreadField = inputs.field;
 | 
| +    var xField = this.contentElement.createChild("div", "shadow-editor-field");
 | 
| +    this._xInput = this._createTextInput(xField, WebInspector.UIString("X offset"));
 | 
| +    var yField = this.contentElement.createChild("div", "shadow-editor-field");
 | 
| +    this._yInput = this._createTextInput(yField, WebInspector.UIString("Y offset"));
 | 
| +    this._xySlider = xField.createChild("canvas", "shadow-editor-2D-slider");
 | 
| +    this._xySlider.width = WebInspector.CSSShadowEditor.canvasSize;
 | 
| +    this._xySlider.height = WebInspector.CSSShadowEditor.canvasSize;
 | 
| +    this._xySlider.tabIndex = -1;
 | 
| +    this._halfCanvasSize = WebInspector.CSSShadowEditor.canvasSize / 2;
 | 
| +    this._innerCanvasSize = this._halfCanvasSize - WebInspector.CSSShadowEditor.sliderThumbRadius;
 | 
| +    WebInspector.installDragHandle(this._xySlider, this._dragStart.bind(this), this._dragMove.bind(this), null, "default");
 | 
| +    this._xySlider.addEventListener("keydown", this._onCanvasArrowKey.bind(this), false);
 | 
| +    this._xySlider.addEventListener("blur", this._onCanvasBlur.bind(this), false);
 | 
| +
 | 
| +    var blurField = this.contentElement.createChild("div", "shadow-editor-blur-field");
 | 
| +    this._blurInput = this._createTextInput(blurField, WebInspector.UIString("Blur"));
 | 
| +    this._blurSlider = this._createSlider(blurField);
 | 
| +
 | 
| +    this._spreadField = this.contentElement.createChild("div", "shadow-editor-field");
 | 
| +    this._spreadInput = this._createTextInput(this._spreadField, WebInspector.UIString("Spread"));
 | 
| +    this._spreadSlider = this._createSlider(this._spreadField);
 | 
|  }
 | 
|  
 | 
|  /** @enum {symbol} */
 | 
| @@ -43,19 +50,22 @@ WebInspector.CSSShadowEditor.Events = {
 | 
|  }
 | 
|  
 | 
|  /** @type {number} */
 | 
| -WebInspector.CSSShadowEditor.maxRange = 40;
 | 
| +WebInspector.CSSShadowEditor.maxRange = 20;
 | 
|  /** @type {string} */
 | 
|  WebInspector.CSSShadowEditor.defaultUnit = "px";
 | 
| +/** @type {number} */
 | 
| +WebInspector.CSSShadowEditor.sliderThumbRadius = 6;
 | 
| +/** @type {number} */
 | 
| +WebInspector.CSSShadowEditor.canvasSize = 88;
 | 
|  
 | 
|  WebInspector.CSSShadowEditor.prototype = {
 | 
|      /**
 | 
| +     * @param {!Element} field
 | 
|       * @param {string} propertyName
 | 
| -     * @param {boolean} negativeAllowed
 | 
| -     * @return {{textInput: !Element, rangeInput: !Element, field: !Element}}
 | 
| +     * @return {!Element}
 | 
|       */
 | 
| -    _createSliderField: function(propertyName, negativeAllowed)
 | 
| +    _createTextInput: function(field, propertyName)
 | 
|      {
 | 
| -        var field = this.contentElement.createChild("div", "shadow-editor-field");
 | 
|          var label = field.createChild("label", "shadow-editor-label");
 | 
|          label.textContent = propertyName;
 | 
|          label.setAttribute("for", propertyName);
 | 
| @@ -66,11 +76,19 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|          textInput.addEventListener("mousewheel", this._handleValueModification.bind(this), false);
 | 
|          textInput.addEventListener("input", this._onTextInput.bind(this), false);
 | 
|          textInput.addEventListener("blur", this._onTextBlur.bind(this), false);
 | 
| -        var halfRange = WebInspector.CSSShadowEditor.maxRange / 2;
 | 
| -        var slider = negativeAllowed ? createSliderLabel(-halfRange, halfRange) : createSliderLabel(0, WebInspector.CSSShadowEditor.maxRange);
 | 
| +        return textInput;
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!Element} field
 | 
| +     * @return {!Element}
 | 
| +     */
 | 
| +    _createSlider: function(field)
 | 
| +    {
 | 
| +        var slider = createSliderLabel(0, WebInspector.CSSShadowEditor.maxRange, -1);
 | 
|          slider.addEventListener("input", this._onSliderInput.bind(this), false);
 | 
|          field.appendChild(slider);
 | 
| -        return {field: field, textInput: textInput, rangeInput: slider};
 | 
| +        return slider;
 | 
|      },
 | 
|  
 | 
|      /**
 | 
| @@ -99,10 +117,9 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|          this._yInput.value = this._model.offsetY().asCSSText();
 | 
|          this._blurInput.value = this._model.blurRadius().asCSSText();
 | 
|          this._spreadInput.value = this._model.spreadRadius().asCSSText();
 | 
| -        this._xSlider.value = this._model.offsetX().amount;
 | 
| -        this._ySlider.value = this._model.offsetY().amount;
 | 
|          this._blurSlider.value = this._model.blurRadius().amount;
 | 
|          this._spreadSlider.value = this._model.spreadRadius().amount;
 | 
| +        this._updateCanvas(false);
 | 
|      },
 | 
|  
 | 
|      _updateButtons: function()
 | 
| @@ -112,6 +129,50 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|      },
 | 
|  
 | 
|      /**
 | 
| +     * @param {boolean} drawFocus
 | 
| +     */
 | 
| +    _updateCanvas: function(drawFocus)
 | 
| +    {
 | 
| +        var context = this._xySlider.getContext("2d");
 | 
| +        context.clearRect(0, 0, this._xySlider.width, this._xySlider.height);
 | 
| +
 | 
| +        // Draw dashed axes.
 | 
| +        context.save();
 | 
| +        context.setLineDash([1, 1]);
 | 
| +        context.strokeStyle = "rgba(0, 0, 0, 0.2)";
 | 
| +        context.beginPath();
 | 
| +        context.moveTo(this._halfCanvasSize, 0);
 | 
| +        context.lineTo(this._halfCanvasSize, WebInspector.CSSShadowEditor.canvasSize);
 | 
| +        context.moveTo(0, this._halfCanvasSize);
 | 
| +        context.lineTo(WebInspector.CSSShadowEditor.canvasSize, this._halfCanvasSize);
 | 
| +        context.stroke();
 | 
| +        context.restore();
 | 
| +
 | 
| +        var thumbPoint = this._sliderThumbPosition();
 | 
| +        // Draw 2D slider line.
 | 
| +        context.save();
 | 
| +        context.translate(this._halfCanvasSize, this._halfCanvasSize);
 | 
| +        context.lineWidth = 2;
 | 
| +        context.strokeStyle = "rgba(0, 0, 0, 0.3)";
 | 
| +        context.beginPath();
 | 
| +        context.moveTo(0, 0);
 | 
| +        context.lineTo(thumbPoint.x, thumbPoint.y);
 | 
| +        context.stroke();
 | 
| +        // Draw 2D slider thumb.
 | 
| +        if (drawFocus) {
 | 
| +            context.beginPath();
 | 
| +            context.fillStyle = "rgba(66, 133, 244, 0.4)";
 | 
| +            context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.sliderThumbRadius + 2, 0, 2 * Math.PI);
 | 
| +            context.fill();
 | 
| +        }
 | 
| +        context.beginPath()
 | 
| +        context.fillStyle = "#4285F4";
 | 
| +        context.arc(thumbPoint.x, thumbPoint.y, WebInspector.CSSShadowEditor.sliderThumbRadius, 0, 2 * Math.PI);
 | 
| +        context.fill();
 | 
| +        context.restore();
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
|       * @param {!Event} event
 | 
|       */
 | 
|      _onButtonClick: function(event)
 | 
| @@ -163,16 +224,16 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|      _onTextInput: function(event)
 | 
|      {
 | 
|          this._changedElement = event.currentTarget;
 | 
| -        this._changedElement.classList.toggle("invalid", false);
 | 
| +        this._changedElement.classList.remove("invalid");
 | 
|          var length = WebInspector.CSSLength.parse(event.currentTarget.value);
 | 
|          if (!length || event.currentTarget === this._blurInput && length.amount < 0)
 | 
|              return;
 | 
|          if (event.currentTarget === this._xInput) {
 | 
|              this._model.setOffsetX(length);
 | 
| -            this._xSlider.value = length.amount;
 | 
| +            this._updateCanvas(false);
 | 
|          } else if (event.currentTarget === this._yInput) {
 | 
|              this._model.setOffsetY(length);
 | 
| -            this._ySlider.value = length.amount;
 | 
| +            this._updateCanvas(false);
 | 
|          } else if (event.currentTarget === this._blurInput) {
 | 
|              this._model.setBlurRadius(length);
 | 
|              this._blurSlider.value = length.amount;
 | 
| @@ -187,7 +248,7 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|      {
 | 
|          if (!this._changedElement)
 | 
|              return;
 | 
| -        var length = !this._changedElement.value ? WebInspector.CSSLength.zero() : WebInspector.CSSLength.parse(this._changedElement.value);
 | 
| +        var length = !this._changedElement.value.trim() ? WebInspector.CSSLength.zero() : WebInspector.CSSLength.parse(this._changedElement.value);
 | 
|          if (!length)
 | 
|              length = WebInspector.CSSLength.parse(this._changedElement.value + WebInspector.CSSShadowEditor.defaultUnit);
 | 
|          if (!length) {
 | 
| @@ -198,11 +259,11 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|          if (this._changedElement === this._xInput) {
 | 
|              this._model.setOffsetX(length);
 | 
|              this._xInput.value = length.asCSSText();
 | 
| -            this._xSlider.value = length.amount;
 | 
| +            this._updateCanvas(false);
 | 
|          } else if (this._changedElement === this._yInput) {
 | 
|              this._model.setOffsetY(length);
 | 
|              this._yInput.value = length.asCSSText();
 | 
| -            this._ySlider.value = length.amount;
 | 
| +            this._updateCanvas(false);
 | 
|          } else if (this._changedElement === this._blurInput) {
 | 
|              if (length.amount < 0)
 | 
|                  length = WebInspector.CSSLength.zero();
 | 
| @@ -223,25 +284,121 @@ WebInspector.CSSShadowEditor.prototype = {
 | 
|       */
 | 
|      _onSliderInput: function(event)
 | 
|      {
 | 
| -        if (event.currentTarget === this._xSlider) {
 | 
| -            this._model.setOffsetX(new WebInspector.CSSLength(this._xSlider.value, this._model.offsetX().unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
| -            this._xInput.value = this._model.offsetX().asCSSText();
 | 
| -            this._xInput.classList.toggle("invalid", false);
 | 
| -        } else if (event.currentTarget === this._ySlider) {
 | 
| -            this._model.setOffsetY(new WebInspector.CSSLength(this._ySlider.value, this._model.offsetY().unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
| -            this._yInput.value = this._model.offsetY().asCSSText();
 | 
| -            this._yInput.classList.toggle("invalid", false);
 | 
| -        } else if (event.currentTarget === this._blurSlider) {
 | 
| +        if (event.currentTarget === this._blurSlider) {
 | 
|              this._model.setBlurRadius(new WebInspector.CSSLength(this._blurSlider.value, this._model.blurRadius().unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
|              this._blurInput.value = this._model.blurRadius().asCSSText();
 | 
| -            this._blurInput.classList.toggle("invalid", false);
 | 
| +            this._blurInput.classList.remove("invalid");
 | 
|          } else if (event.currentTarget === this._spreadSlider) {
 | 
|              this._model.setSpreadRadius(new WebInspector.CSSLength(this._spreadSlider.value, this._model.spreadRadius().unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
|              this._spreadInput.value = this._model.spreadRadius().asCSSText();
 | 
| -            this._spreadInput.classList.toggle("invalid", false);
 | 
| +            this._spreadInput.classList.remove("invalid");
 | 
|          }
 | 
|          this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChanged, this._model);
 | 
|      },
 | 
|  
 | 
| +    /**
 | 
| +     * @param {!Event} event
 | 
| +     * @return {boolean}
 | 
| +     */
 | 
| +    _dragStart: function(event)
 | 
| +    {
 | 
| +        this._xySlider.focus();
 | 
| +        this._updateCanvas(true);
 | 
| +        this._canvasOrigin = new WebInspector.Geometry.Point(this._xySlider.totalOffsetLeft() + this._halfCanvasSize, this._xySlider.totalOffsetTop() + this._halfCanvasSize);
 | 
| +        var clickedPoint = new WebInspector.Geometry.Point(event.x - this._canvasOrigin.x, event.y - this._canvasOrigin.y);
 | 
| +        var thumbPoint = this._sliderThumbPosition();
 | 
| +        if (clickedPoint.distanceTo(thumbPoint) >= WebInspector.CSSShadowEditor.sliderThumbRadius)
 | 
| +            this._dragMove(event);
 | 
| +        return true;
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!Event} event
 | 
| +     */
 | 
| +    _dragMove: function(event)
 | 
| +    {
 | 
| +        var point = new WebInspector.Geometry.Point(event.x - this._canvasOrigin.x, event.y - this._canvasOrigin.y);
 | 
| +        var constrainedPoint = this._constrainPoint(point, this._innerCanvasSize);
 | 
| +        var newX = Math.round((constrainedPoint.x / this._innerCanvasSize) * WebInspector.CSSShadowEditor.maxRange);
 | 
| +        var newY = Math.round((constrainedPoint.y / this._innerCanvasSize) * WebInspector.CSSShadowEditor.maxRange);
 | 
| +        this._model.setOffsetX(new WebInspector.CSSLength(newX, this._model.offsetX().unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
| +        this._model.setOffsetY(new WebInspector.CSSLength(newY, this._model.offsetY().unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
| +        this._xInput.value = this._model.offsetX().asCSSText();
 | 
| +        this._yInput.value = this._model.offsetY().asCSSText();
 | 
| +        this._xInput.classList.remove("invalid");
 | 
| +        this._yInput.classList.remove("invalid");
 | 
| +        this._updateCanvas(true);
 | 
| +        this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChanged, this._model);
 | 
| +    },
 | 
| +
 | 
| +    _onCanvasBlur: function()
 | 
| +    {
 | 
| +        this._updateCanvas(false);
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!Event} event
 | 
| +     */
 | 
| +    _onCanvasArrowKey: function(event)
 | 
| +    {
 | 
| +        var shiftX = 0;
 | 
| +        var shiftY = 0;
 | 
| +        if (event.key === "ArrowRight")
 | 
| +            shiftX = 1;
 | 
| +        else if (event.key === "ArrowLeft")
 | 
| +            shiftX = -1;
 | 
| +        else if (event.key === "ArrowUp")
 | 
| +            shiftY = -1;
 | 
| +        else if (event.key === "ArrowDown")
 | 
| +            shiftY = 1;
 | 
| +
 | 
| +        if (!shiftX && !shiftY)
 | 
| +            return;
 | 
| +        event.consume(true);
 | 
| +
 | 
| +        if (shiftX) {
 | 
| +            var offsetX = this._model.offsetX();
 | 
| +            var newAmount = Number.constrain(offsetX.amount + shiftX, -WebInspector.CSSShadowEditor.maxRange, WebInspector.CSSShadowEditor.maxRange);
 | 
| +            if (newAmount === offsetX.amount)
 | 
| +                return;
 | 
| +            this._model.setOffsetX(new WebInspector.CSSLength(newAmount, offsetX.unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
| +            this._xInput.value = this._model.offsetX().asCSSText();
 | 
| +            this._xInput.classList.remove("invalid");
 | 
| +        }
 | 
| +        if (shiftY) {
 | 
| +            var offsetY = this._model.offsetY();
 | 
| +            var newAmount = Number.constrain(offsetY.amount + shiftY, -WebInspector.CSSShadowEditor.maxRange, WebInspector.CSSShadowEditor.maxRange);
 | 
| +            if (newAmount === offsetY.amount)
 | 
| +                return;
 | 
| +            this._model.setOffsetY(new WebInspector.CSSLength(newAmount, offsetY.unit || WebInspector.CSSShadowEditor.defaultUnit));
 | 
| +            this._yInput.value = this._model.offsetY().asCSSText();
 | 
| +            this._yInput.classList.remove("invalid");
 | 
| +        }
 | 
| +        this._updateCanvas(true);
 | 
| +        this.dispatchEventToListeners(WebInspector.CSSShadowEditor.Events.ShadowChanged, this._model);
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * @param {!WebInspector.Geometry.Point} point
 | 
| +     * @param {number} max
 | 
| +     * @return {!WebInspector.Geometry.Point}
 | 
| +     */
 | 
| +    _constrainPoint: function(point, max)
 | 
| +    {
 | 
| +        if (Math.abs(point.x) <= max && Math.abs(point.y) <= max)
 | 
| +            return new WebInspector.Geometry.Point(point.x, point.y);
 | 
| +        return point.scale(max / Math.max(Math.abs(point.x), Math.abs(point.y)));
 | 
| +    },
 | 
| +
 | 
| +    /**
 | 
| +     * @return {!WebInspector.Geometry.Point}
 | 
| +     */
 | 
| +    _sliderThumbPosition: function()
 | 
| +    {
 | 
| +        var x = (this._model.offsetX().amount / WebInspector.CSSShadowEditor.maxRange) * this._innerCanvasSize;
 | 
| +        var y = (this._model.offsetY().amount / WebInspector.CSSShadowEditor.maxRange) * this._innerCanvasSize;
 | 
| +        return this._constrainPoint(new WebInspector.Geometry.Point(x, y), this._innerCanvasSize);
 | 
| +    },
 | 
| +
 | 
|      __proto__: WebInspector.VBox.prototype
 | 
| -}
 | 
| +}
 | 
| 
 |