Media Router WebUI: replace paper-slider with cr-slider

Bug: 902873
Change-Id: I7fe0979471ba0206ff1326448e09bf4a5f7f8437
Reviewed-on: https://chromium-review.googlesource.com/c/1362245
Commit-Queue: Esmael El-Moslimany <aee@chromium.org>
Reviewed-by: Takumi Fujimoto <takumif@chromium.org>
Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#618440}
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.css b/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
index 6c7d65c..811d8db7 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.css
@@ -24,7 +24,7 @@
   transform: scaleX(-1);
 }
 
-:host-context([dir='rtl']) #route-volume-slider {
+:host-context([dir='rtl']) #volume-holder {
   transform: scaleX(-1);
 }
 
@@ -51,9 +51,9 @@
 }
 
 #route-time-slider {
-  --paper-slider-knob-color: rgb(16, 16, 16);
-  --paper-slider-active-color: rgb(16, 16, 16);
-  --paper-slider-pin-color: rgb(16, 16, 16);
+  --cr-slider-active-color: rgb(16, 16, 16);
+  --cr-slider-container-color: rgba(16, 16, 16, .24);
+  --cr-slider-knob-color: rgb(16, 16, 16);
   width: 100%;
 }
 
@@ -63,9 +63,9 @@
 }
 
 #route-volume-slider {
-  --paper-slider-knob-color: rgb(16, 16, 16);
-  --paper-slider-active-color: rgb(33, 150, 243);
-  --paper-slider-pin-color: rgb(16, 16, 16);
+  --cr-slider-active-color: rgb(33, 150, 243);
+  --cr-slider-container-color: rgba(16, 16, 16, .24);
+  --cr-slider-knob-color: rgb(16, 16, 16);
   width: 100%;
 }
 
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.html b/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
index 76c08c8a..b46e424 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.html
@@ -1,10 +1,10 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_slider/cr_slider.html">
 <link rel="import" href="chrome://resources/html/md_select_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-slider/paper-slider.html">
 
 <iron-iconset-svg name="route-controls" size="24">
 <svg><defs>
@@ -31,15 +31,15 @@
       </div>
       <div>
         <div id="route-time-controls" hidden="[[!routeStatus.canSeek]]">
-          <paper-slider
+          <cr-slider
               aria-valuetext$="[[getTimeSliderValueText_(displayedCurrentTime_)]]"
               dir="ltr"
               id="route-time-slider"
-              on-change="onSeekComplete_"
-              on-immediate-value-change="onSeekByDragging_"
-              min="0" max="[[routeStatus.duration]]" step="1"
+              on-dragging-changed="onSeekingChanged_"
+              on-cr-slider-value-changed="onSeekSliderValueChanged_"
+              min="0" max="[[routeStatus.duration]]"
               title="[[i18n('seekTitle')]]"
-              value="[[displayedCurrentTime_]]"></paper-slider>
+              value="[[displayedCurrentTime_]]"></cr-slider>
           <div id="timeline">
             <span id="current-time"
                   aria-label$="[[getCurrentTimeLabel_(displayedCurrentTime_)]]">
@@ -69,15 +69,13 @@
                 on-click="onMuteUnmute_"></paper-icon-button>
           </span>
           <span id="volume-holder" hidden="[[!routeStatus.canSetVolume]]">
-            <paper-slider
+            <cr-slider
                 aria-valuetext$="[[getVolumeSliderValueText_(displayedVolume_)]]"
                 id="route-volume-slider"
                 disabled="[[!routeStatus.canSetVolume]]"
-                on-change="onVolumeChangeComplete_"
-                on-immediate-value-change="onVolumeChangeByDragging_"
-                title="[[i18n('volumeTitle')]]"
-                value="[[displayedVolume_]]"
-                min="0" max="1" step="0.01"></paper-slider>
+                on-cr-slider-value-changed="onVolumeChanged_"
+                on-dragging-changed="onVolumeDraggingChanged_"
+                title="[[i18n('volumeTitle')]]"></cr-slider>
           </span>
           <div id="hangouts-local-present-controls"
                hidden="[[!routeStatus.hangoutsExtraData]]">
diff --git a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
index fd88414..dff04a5 100644
--- a/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
+++ b/chrome/browser/resources/media_router/elements/route_controls/route_controls.js
@@ -378,7 +378,9 @@
       this.displayedCurrentTime_ = newRouteStatus.currentTime;
     }
     if (this.shouldAcceptVolumeUpdates_()) {
-      this.displayedVolume_ = Math.round(newRouteStatus.volume * 100) / 100;
+      const volume = Math.round(newRouteStatus.volume * 100);
+      this.$['route-volume-slider'].value = volume;
+      this.displayedVolume_ = volume / 100;
     }
     if (!this.initialLoadTime_) {
       this.initialLoadTime_ = Date.now();
@@ -422,59 +424,63 @@
     }
   },
 
-  /**
-   * Called when the user clicks on or stops dragging the seek bar.
-   * @param {!Event} e
-   * @private
-   */
-  onSeekComplete_: function(e) {
+  /** @private */
+  updateTime_: function() {
     this.stopIncrementingCurrentTime_();
-    this.displayedCurrentTime_ = e.target.value;
-    media_router.browserApi.seekCurrentMedia(this.displayedCurrentTime_);
-    this.isSeeking_ = false;
-    this.lastSeekByUser_ = Date.now();
+    this.displayedCurrentTime_ = this.$['route-time-slider'].value;
+    if (!this.isSeeking_) {
+      media_router.browserApi.seekCurrentMedia(this.displayedCurrentTime_);
+      this.lastSeekByUser_ = Date.now();
+    }
   },
 
   /**
-   * Called while the user is dragging the seek bar.
-   * @param {!Event} e
+   * @param {!{detail: {value: boolean}}} e
    * @private
    */
-  onSeekByDragging_: function(e) {
-    this.isSeeking_ = true;
-    var target = /** @type {{immediateValue: number}} */ (e.target);
-    this.displayedCurrentTime_ = target.immediateValue;
+  onSeekingChanged_: function(e) {
+    this.isSeeking_ = e.detail.value;
+    this.updateTime_();
   },
 
-  /**
-   * Called when the user clicks on or stops dragging the volume bar.
-   * @param {!Event} e
-   * @private
-   */
-  onVolumeChangeComplete_: function(e) {
-    this.displayedVolume_ = e.target.value;
-    media_router.browserApi.setCurrentMediaVolume(this.displayedVolume_);
-    this.isVolumeChanging_ = false;
+  /** @private */
+  onSeekSliderValueChanged_: function() {
+    this.updateTime_();
+  },
+
+  /** @private */
+  updateVolume_: function() {
     this.lastVolumeChangeByUser_ = Date.now();
+    const volume = this.$['route-volume-slider'].value / 100;
+    if (volume == this.displayedVolume_)
+      return;
+    this.displayedVolume_ = volume;
+    media_router.browserApi.setCurrentMediaVolume(volume);
   },
 
   /**
-   * Called while the user is dragging the volume bar.
-   * @param {!Event} e
+   * Called when the user updates volume with the slider.
    * @private
    */
-  onVolumeChangeByDragging_: function(e) {
+  onVolumeChanged_: function() {
     /** @const */ var currentTime = Date.now();
     // We limit the frequency of volume change requests during dragging to
     // limit the number of Mojo calls to the component extension.
-    if (currentTime - this.lastVolumeChangeByUser_ < 300) {
+    if (currentTime - this.lastVolumeChangeByUser_ < 300)
       return;
-    }
-    this.lastVolumeChangeByUser_ = currentTime;
-    this.isVolumeChanging_ = true;
-    var target = /** @type {{immediateValue: number}} */ (e.target);
-    this.displayedVolume_ = target.immediateValue;
-    media_router.browserApi.setCurrentMediaVolume(this.displayedVolume_);
+    this.updateVolume_();
+  },
+
+  /**
+   * @param {!{detail: {value: boolean}}} e
+   * @private
+   */
+  onVolumeDraggingChanged_: function(e) {
+    if (!!this.isVolumeChanging_ == !!e.detail.value)
+      return;
+    this.isVolumeChanging_ = e.detail.value;
+    if (!this.isVolumeChanging_)
+      this.updateVolume_();
   },
 
   /**
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
index a401598..cfcb774e 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler.cc
@@ -964,13 +964,13 @@
 void MediaRouterWebUIMessageHandler::OnSeekCurrentMedia(
     const base::ListValue* args) {
   const base::DictionaryValue* args_dict = nullptr;
-  int time;
+  double time;
   if (!args->GetDictionary(0, &args_dict) ||
-      !args_dict->GetInteger("time", &time)) {
+      !args_dict->GetDouble("time", &time)) {
     DVLOG(1) << "Unable to extract time";
     return;
   }
-  base::TimeDelta time_delta = base::TimeDelta::FromSeconds(time);
+  base::TimeDelta time_delta = base::TimeDelta::FromSecondsD(time);
   MediaRouteController* route_controller =
       media_router_ui_->GetMediaRouteController();
   if (route_controller && current_media_status_ &&
diff --git a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
index 17268d1..57df585 100644
--- a/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_webui_message_handler_unittest.cc
@@ -617,9 +617,9 @@
   args_list.Append(std::make_unique<base::DictionaryValue>());
   args_list.GetDictionary(0, &args);
 
-  const int time = 50;
-  args->SetInteger("time", time);
-  EXPECT_CALL(*controller, Seek(base::TimeDelta::FromSeconds(time)));
+  const double time = 50.1;
+  args->SetDouble("time", time);
+  EXPECT_CALL(*controller, Seek(base::TimeDelta::FromSecondsD(time)));
   handler_->OnSeekCurrentMedia(&args_list);
 
   args->Clear();
@@ -666,9 +666,9 @@
   args_list.GetDictionary(0, &args);
 
   // Seek positions greater than the duration or negative should be ignored.
-  args->SetInteger("time", 101);
+  args->SetDouble("time", 101);
   handler_->OnSeekCurrentMedia(&args_list);
-  args->SetInteger("time", -10);
+  args->SetDouble("time", -10);
   handler_->OnSeekCurrentMedia(&args_list);
 
   args->Clear();
diff --git a/chrome/test/data/webui/cr_elements/cr_slider_test.js b/chrome/test/data/webui/cr_elements/cr_slider_test.js
index ee7dc8ab..807a37f 100644
--- a/chrome/test/data/webui/cr_elements/cr_slider_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_slider_test.js
@@ -147,6 +147,7 @@
 
   test('snaps to closest value', () => {
     crSlider.snaps = true;
+    crSlider.ticks = [];
     pointerDown(.501);
     assertEquals(50, crSlider.value);
     pointerMove(.505);
diff --git a/chrome/test/data/webui/media_router/route_controls_tests.js b/chrome/test/data/webui/media_router/route_controls_tests.js
index efc7727..b142590 100644
--- a/chrome/test/data/webui/media_router/route_controls_tests.js
+++ b/chrome/test/data/webui/media_router/route_controls_tests.js
@@ -199,7 +199,7 @@
         MockInteractions.tap(controls.$$('#route-volume-button'));
       });
 
-      // // Tests that the seek slider sends a command to the browser API.
+      // Tests that the seek slider sends a command to the browser API.
       test('send seek command', function(done) {
         var currentTime = 500;
         var duration = 1200;
@@ -222,16 +222,16 @@
         // In actual usage, the change event gets fired when the user interacts
         // with the slider.
         controls.$$('#route-time-slider').value = currentTime;
-        controls.$$('#route-time-slider').fire('change');
+        controls.$$('#route-time-slider').fire('cr-slider-value-changed');
       });
 
       // Tests that the volume slider sends a command to the browser API.
       test('send set volume command', function(done) {
-        var volume = 0.45;
+        var volume = 45;
         var waitForSetVolumeEvent = function(data) {
           document.removeEventListener(
               'mock-set-current-media-volume', waitForSetVolumeEvent);
-          if (data.detail.volume == volume) {
+          if (data.detail.volume == volume / 100) {
             done();
           } else {
             done(
@@ -247,7 +247,7 @@
         // In actual usage, the change event gets fired when the user interacts
         // with the slider.
         controls.$$('#route-volume-slider').value = volume;
-        controls.$$('#route-volume-slider').fire('change');
+        controls.$$('#route-volume-slider').fire('cr-slider-value-changed');
       });
 
       test('increment current time while playing', function(done) {
@@ -329,7 +329,7 @@
       test('ignore external updates right after using sliders', function(done) {
         var currentTime = 500;
         var externalCurrentTime = 800;
-        var volume = 0.45;
+        var volume = 45;
         var externalVolume = 0.72;
         var duration = 1200;
         var doExternalUpdate = function() {
@@ -348,9 +348,9 @@
         // In actual usage, the change event gets fired when the user interacts
         // with the slider.
         controls.$$('#route-time-slider').value = currentTime;
-        controls.$$('#route-time-slider').fire('change');
+        controls.$$('#route-time-slider').fire('cr-slider-value-changed');
         controls.$$('#route-volume-slider').value = volume;
-        controls.$$('#route-volume-slider').fire('change');
+        controls.$$('#route-volume-slider').fire('cr-slider-value-changed');
 
         // External updates right after slider interaction should be ignored.
         doExternalUpdate();
@@ -363,7 +363,7 @@
           assertEquals(
               controls.$$('#route-time-slider').value, externalCurrentTime);
           assertEquals(
-              controls.$$('#route-volume-slider').value, externalVolume);
+              controls.$$('#route-volume-slider').value, externalVolume * 100);
           done();
         }, 1001);
       });
diff --git a/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js b/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js
index 328f071d..3faa479 100644
--- a/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js
+++ b/ui/webui/resources/cr_elements/cr_radio_group/cr_radio_group.js
@@ -76,7 +76,7 @@
 
     /** @override */
     attached: function() {
-      this.isRtl_ = this.matches(':host-context([dir=rtl]) cr-slider');
+      this.isRtl_ = this.matches(':host-context([dir=rtl]) cr-radio-group');
       this.deltaKeyMap_ = new Map([
         ['ArrowDown', 1],
         ['ArrowLeft', this.isRtl_ ? 1 : -1],
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
index bf0fb808..2dda9f82 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.html
@@ -38,7 +38,8 @@
       }
 
       #barContainer {
-        border-top-color: var(--cr-slider-container-color, var(--google-blue-600-opacity-24));
+        border-top-color: var(--cr-slider-container-color,
+            var(--google-blue-600-opacity-24));
         height: var(--cr-slider-bar-height);
         margin: 0 16px;
         position: absolute;
@@ -52,7 +53,6 @@
 
       #bar {
         border-top-color: var(--cr-slider-active-color, var(--google-blue-600));
-        left: 0;
         position: absolute;
         width: 0;
       }
@@ -61,11 +61,6 @@
         transition: width var(--cr-slider-position-transition);
       }
 
-      :host([is-rtl_]) #bar {
-        left: initial;
-        right: 0;
-      }
-
       #knobContainer {
         margin-inline-start: 12px;
         position: absolute;
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
index c6e725e..7c11687 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.js
@@ -283,16 +283,21 @@
       if (event.metaKey || event.shiftKey || event.altKey || event.ctrlKey)
         return;
 
+      /** @type {number|undefined} */
+      let newValue;
       if (event.key == 'Home') {
-        this.updateValue_(this.min);
+        newValue = this.min;
       } else if (event.key == 'End') {
-        this.updateValue_(this.max);
+        newValue = this.max;
       } else if (this.deltaKeyMap_.has(event.key)) {
-        this.updateValue_(this.value + this.deltaKeyMap_.get(event.key));
-      } else {
-        return;
+        newValue = this.value + this.deltaKeyMap_.get(event.key);
       }
 
+      if (newValue == undefined)
+        return;
+
+      if (this.updateValue_(newValue))
+        this.fire('cr-slider-value-changed');
       event.preventDefault();
       event.stopPropagation();
       setTimeout(() => {
@@ -364,9 +369,7 @@
 
     /** @private */
     onTicksChanged_: function() {
-      if (this.ticks.length == 0) {
-        this.snaps = false;
-      } else if (this.ticks.length > 1) {
+      if (this.ticks.length > 1) {
         this.snaps = true;
         this.max = this.ticks.length - 1;
         this.min = 0;
@@ -423,15 +426,17 @@
 
     /**
      * @param {number} value
+     * @return {boolean}
      * @private
      */
     updateValue_: function(value) {
-      let validValue = clamp(this.min, this.max, value);
-      validValue = this.snaps ? Math.round(validValue) : validValue;
-      if (this.value != validValue) {
-        this.value = validValue;
-        this.fire('cr-slider-value-changed');
+      value = this.snaps ? Math.round(value) : value;
+      value = clamp(this.min, this.max, value);
+      if (this.value == value) {
+        return false;
       }
+      this.value = value;
+      return true;
     },
 
     /**
@@ -443,7 +448,8 @@
       let ratio = (clientX - rect.left) / rect.width;
       if (this.isRtl_)
         ratio = 1 - ratio;
-      this.updateValue_(ratio * (this.max - this.min) + this.min);
+      if (this.updateValue_(ratio * (this.max - this.min) + this.min))
+        this.fire('cr-slider-value-changed');
     },
 
     _createRipple: function() {