diff --git a/.gitignore b/.gitignore
index 5ba7c07..719b6b94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,6 +89,7 @@
 /chrome/browser/extensions/default_extensions/chromeos
 /chrome/browser/google/linkdoctor_internal
 /chrome/browser/internal
+/chrome/browser/media/engagement_internal
 /chrome/browser/performance_monitor/performance_monitor.xml
 /chrome/browser/protector/internal
 /chrome/browser/resources/chromeos/quickoffice
diff --git a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
index e2b46c45..2de9436 100644
--- a/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
+++ b/chrome/browser/notifications/platform_notification_service_interactive_uitest.cc
@@ -260,6 +260,89 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceBrowserTest,
+                       NonPersistentWebNotificationOptionsReflection) {
+  ASSERT_NO_FATAL_FAILURE(GrantNotificationPermissionForTest());
+
+  // First, test the default values.
+
+  std::string script_result;
+  ASSERT_TRUE(RunScript("DisplayNonPersistentNotification('Title', {})",
+                        &script_result));
+  EXPECT_EQ("ok", script_result);
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications(false /* is_persistent */);
+  ASSERT_EQ(1u, notifications.size());
+
+  // We don't use the notification's direction or language, hence we don't check
+  // those properties here.
+  const message_center::Notification& default_notification = notifications[0];
+  EXPECT_EQ("Title", base::UTF16ToUTF8(default_notification.title()));
+  EXPECT_EQ("", base::UTF16ToUTF8(default_notification.message()));
+  EXPECT_TRUE(default_notification.image().IsEmpty());
+  EXPECT_TRUE(default_notification.icon().IsEmpty());
+  EXPECT_TRUE(default_notification.small_image().IsEmpty());
+  EXPECT_FALSE(default_notification.renotify());
+  EXPECT_FALSE(default_notification.silent());
+  EXPECT_FALSE(default_notification.never_timeout());
+  EXPECT_EQ(0u, default_notification.buttons().size());
+
+  // Verifies that the notification's default timestamp is set in the last 30
+  // seconds. This number has no significance, but it needs to be significantly
+  // high to avoid flakiness in the test.
+  EXPECT_NEAR(default_notification.timestamp().ToJsTime(),
+              base::Time::Now().ToJsTime(), 30 * 1000);
+
+  // Now, test the non-default values.
+
+  ASSERT_TRUE(RunScript(R"(DisplayNonPersistentNotification('Title', {
+          body: 'Contents',
+          tag: 'replace-id',
+          dir: 'rtl',
+          lang: 'nl-NL',
+          image: 'icon.png',
+          icon: 'icon.png',
+          badge: 'icon.png',
+          timestamp: 621046800000,
+          renotify: true,
+          silent: true,
+          requireInteraction: true,
+          data: [
+            { property: 'value' }
+          ]
+        }))",
+                        &script_result));
+  EXPECT_EQ("ok", script_result);
+
+  notifications = GetDisplayedNotifications(false /* is_persistent */);
+  ASSERT_EQ(2u, notifications.size());
+
+  // We don't use the notification's direction or language, hence we don't check
+  // those properties here.
+  const message_center::Notification& all_options_notification =
+      notifications[1];
+  EXPECT_EQ("Title", base::UTF16ToUTF8(all_options_notification.title()));
+  EXPECT_EQ("Contents", base::UTF16ToUTF8(all_options_notification.message()));
+  // The js-provided tag should be part of the id.
+  EXPECT_FALSE(all_options_notification.id().find("replace-id") ==
+               std::string::npos);
+#if !defined(OS_MACOSX)
+  EXPECT_FALSE(all_options_notification.image().IsEmpty());
+  EXPECT_EQ(kIconWidth, all_options_notification.image().Width());
+  EXPECT_EQ(kIconHeight, all_options_notification.image().Height());
+#endif
+  EXPECT_FALSE(all_options_notification.icon().IsEmpty());
+  EXPECT_EQ(kIconWidth, all_options_notification.icon().Width());
+  EXPECT_EQ(kIconHeight, all_options_notification.icon().Height());
+  EXPECT_TRUE(all_options_notification.small_image().IsEmpty());
+  EXPECT_TRUE(all_options_notification.renotify());
+  EXPECT_TRUE(all_options_notification.silent());
+  EXPECT_TRUE(all_options_notification.never_timeout());
+  EXPECT_DOUBLE_EQ(kNotificationTimestamp,
+                   all_options_notification.timestamp().ToJsTime());
+}
+
+IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceBrowserTest,
                        WebNotificationOptionsReflection) {
   ASSERT_NO_FATAL_FAILURE(GrantNotificationPermissionForTest());
 
@@ -274,7 +357,8 @@
       GetDisplayedNotifications(true /* is_persistent */);
   ASSERT_EQ(1u, notifications.size());
 
-  // We don't use or check the notification's direction and language.
+  // We don't use the notification's direction or language, hence we don't check
+  // those properties here.
   const message_center::Notification& default_notification = notifications[0];
   EXPECT_EQ("Some title", base::UTF16ToUTF8(default_notification.title()));
   EXPECT_EQ("", base::UTF16ToUTF8(default_notification.message()));
@@ -301,7 +385,8 @@
   notifications = GetDisplayedNotifications(true /* is_persistent */);
   ASSERT_EQ(2u, notifications.size());
 
-  // We don't use or check the notification's direction and language.
+  // We don't use the notification's direction or language, hence we don't check
+  // those properties here.
   const message_center::Notification& all_options_notification =
       notifications[1];
   EXPECT_EQ("Title", base::UTF16ToUTF8(all_options_notification.title()));
@@ -877,3 +962,95 @@
   // notification should be shown without an image.
   EXPECT_TRUE(notifications[0].image().IsEmpty());
 }
+
+class PlatformNotificationServiceMojoEnabledBrowserTest
+    : public PlatformNotificationServiceBrowserTest {
+ public:
+  // InProcessBrowserTest overrides.
+  void SetUpInProcessBrowserTestFixture() override {
+    scoped_feature_list_.InitWithFeatures({features::kNotificationsWithMojo},
+                                          {});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PlatformNotificationServiceMojoEnabledBrowserTest,
+                       NonPersistentWebNotificationOptionsReflection) {
+  ASSERT_NO_FATAL_FAILURE(GrantNotificationPermissionForTest());
+
+  // First, test the default values.
+
+  std::string script_result;
+  {
+    // This closure ensures the notification has been shown before we check it.
+    // TODO(crbug.com/595685): We can dispense with this closure once the show
+    // event is implemented via mojo.
+    base::RunLoop run_loop;
+    display_service_tester_->SetNotificationAddedClosure(
+        run_loop.QuitClosure());
+
+    ASSERT_TRUE(RunScript(
+        "DisplayNonPersistentNotificationWithoutWaitingForEvent('Title')",
+        &script_result));
+    EXPECT_EQ("sync-ok", script_result);
+
+    run_loop.Run();
+  }
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications(false /* is_persistent */);
+  ASSERT_EQ(1u, notifications.size());
+
+  // We don't use the notification's direction or language, hence we don't check
+  // those properties here.
+  const message_center::Notification& default_notification = notifications[0];
+  EXPECT_EQ("Title", base::UTF16ToUTF8(default_notification.title()));
+  EXPECT_EQ("", base::UTF16ToUTF8(default_notification.message()));
+  EXPECT_TRUE(default_notification.image().IsEmpty());
+  EXPECT_TRUE(default_notification.icon().IsEmpty());
+  EXPECT_TRUE(default_notification.small_image().IsEmpty());
+  EXPECT_FALSE(default_notification.renotify());
+  EXPECT_FALSE(default_notification.silent());
+  EXPECT_FALSE(default_notification.never_timeout());
+  EXPECT_EQ(0u, default_notification.buttons().size());
+  // TODO(https://crbug.com/595685): Test the default notification timestamp.
+
+  // Now, test the non-default values.
+
+  {
+    // This closure ensures the notification has been shown before we check it.
+    // TODO(crbug.com/595685): We can dispense with this closure once the show
+    // event is implemented via mojo.
+    base::RunLoop run_loop;
+    display_service_tester_->SetNotificationAddedClosure(
+        run_loop.QuitClosure());
+    ASSERT_TRUE(RunScript(
+        R"(DisplayNonPersistentNotificationWithoutWaitingForEvent('Title2', {
+          body: 'Contents',
+          tag: 'replace-id',
+          dir: 'rtl',
+          lang: 'nl-NL',
+          image: 'icon.png',
+          icon: 'icon.png',
+          badge: 'icon.png',
+          timestamp: 621046800000,
+          renotify: true,
+          silent: true,
+          requireInteraction: true,
+          data: [
+            { property: 'value' }
+          ]
+        }))",
+        &script_result));
+    EXPECT_EQ("sync-ok", script_result);
+
+    run_loop.Run();
+  }
+
+  notifications = GetDisplayedNotifications(false /* is_persistent */);
+  ASSERT_EQ(2u, notifications.size());
+
+  // TODO(https://crbug.com/595685): Test the rest of the properties.
+}
diff --git a/chrome/test/data/notifications/platform_notification_service.html b/chrome/test/data/notifications/platform_notification_service.html
index 1a7f347..a504667e 100644
--- a/chrome/test/data/notifications/platform_notification_service.html
+++ b/chrome/test/data/notifications/platform_notification_service.html
@@ -21,6 +21,8 @@
         });
       }
 
+      // Instantiates a new non-persistent notification with the given |title|
+      // and |options| and forwards the notification shown or error event.
       function DisplayNonPersistentNotification(title, options) {
         const notification = new Notification(title, options || {});
         notification.addEventListener('show', e =>
@@ -29,6 +31,14 @@
           domAutomationController.send('could not show notification'));
       }
 
+      // Instantiates a new non-persistent notification with the given |title|
+      // and |options| and then forwards script result 'sync-ok', without
+      // waiting for a notification shown or error event.
+      function DisplayNonPersistentNotificationWithoutWaitingForEvent(title, options) {
+        const notification = new Notification(title, options || {});
+        domAutomationController.send('sync-ok');
+      }
+
       // Renews the registered Service Worker registration for this page, then
       // displays a notification on the activated ServiceWorkerRegistration.
       function DisplayPersistentNotification(title, options) {
diff --git a/content/browser/notifications/blink_notification_service_impl.cc b/content/browser/notifications/blink_notification_service_impl.cc
index eca495e..cabdf3e 100644
--- a/content/browser/notifications/blink_notification_service_impl.cc
+++ b/content/browser/notifications/blink_notification_service_impl.cc
@@ -80,15 +80,27 @@
     return;
   PlatformNotificationData platform_notification_data;
   platform_notification_data.title = title;
+
+  // TODO(https://crbug.com/595685): Generate a GUID in the
+  // NotificationIdGenerator instead.
+  static int request_id = 0;
+  request_id++;
+
+  std::string notification_id =
+      notification_context_->notification_id_generator()
+          ->GenerateForNonPersistentNotification(
+              origin_.GetURL(), platform_notification_data.tag, request_id,
+              render_process_id_);
+
   // TODO(crbug.com/595685): Plumb through the rest of the notification data and
   // the notification resources from blink.
   // Using base::Unretained is safe because Service() returns a singleton.
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::BindOnce(&PlatformNotificationService::DisplayNotification,
-                     base::Unretained(Service()), browser_context_, "",
-                     origin_.GetURL(), platform_notification_data,
-                     NotificationResources()));
+                     base::Unretained(Service()), browser_context_,
+                     notification_id, origin_.GetURL(),
+                     platform_notification_data, NotificationResources()));
 }
 
 }  // namespace content
diff --git a/ios/chrome/browser/web/early_page_script_perftest.mm b/ios/chrome/browser/web/early_page_script_perftest.mm
index 7694882a..d6e588ae 100644
--- a/ios/chrome/browser/web/early_page_script_perftest.mm
+++ b/ios/chrome/browser/web/early_page_script_perftest.mm
@@ -42,7 +42,8 @@
 };
 
 // Tests injection time into a bare web view.
-TEST_F(EarlyPageScriptPerfTest, BareWebViewInjection) {
+// TODO(crbug.com/796149): Reenable it.
+TEST_F(EarlyPageScriptPerfTest, FLAKY_BareWebViewInjection) {
   RepeatTimedRuns("Bare web view injection",
                   ^base::TimeDelta(int) {
                     base::ElapsedTimer timer;
diff --git a/third_party/WebKit/LayoutTests/media/controls/modern/singletap-on-outside.html b/third_party/WebKit/LayoutTests/media/controls/modern/singletap-on-outside.html
index ae07b7e5..999ce3c 100644
--- a/third_party/WebKit/LayoutTests/media/controls/modern/singletap-on-outside.html
+++ b/third_party/WebKit/LayoutTests/media/controls/modern/singletap-on-outside.html
@@ -12,18 +12,11 @@
   video.addEventListener('playing', t.step_func(() => {
     // Single tap in the top right hand corner
     const coordinates =
-      coordinatesOutsideElement(mediaControlsOverlayPlayButton(video));
+      coordinatesOutsideElement(mediaControlsOverlayPlayButtonInternal(video));
     singleTapAtCoordinates(coordinates[0] + 1, coordinates[1] + 1);
   }), { once: true });
 
-  video.addEventListener('pause', t.unreached_func());
-
-  video.addEventListener('webkitfullscreenchange', t.unreached_func());
-
-  video.ontimeupdate = t.step_func(() => {
-    if (video.currentTime > 0)
-      t.done();
-  });
+  video.addEventListener('pause', t.step_func_done(), { once: true });
 
   video.play();
 });
diff --git a/third_party/WebKit/LayoutTests/media/controls/modern/slow-doubletap.html b/third_party/WebKit/LayoutTests/media/controls/modern/slow-doubletap.html
index 3ee7af4..8f0471e2 100644
--- a/third_party/WebKit/LayoutTests/media/controls/modern/slow-doubletap.html
+++ b/third_party/WebKit/LayoutTests/media/controls/modern/slow-doubletap.html
@@ -8,23 +8,23 @@
 <script>
 async_test(t => {
   const video = document.querySelector('video');
+  let didPause = false;
 
   video.onplaying = t.step_func(() => {
-    // Double tap in the top right hand corner
-    const coordinates =
-      coordinatesOutsideElement(mediaControlsOverlayPlayButton(video));
-    doubleTapAtCoordinates(coordinates[0] + 1, coordinates[1] + 1, 400);
-  });
-
-  video.addEventListener('pause', t.unreached_func());
-
-  video.addEventListener('webkitfullscreenchange', t.unreached_func());
-
-  video.ontimeupdate = t.step_func(() => {
-    if (video.currentTime > 0)
+    if (didPause) {
       t.done();
+    } else {
+      // Double tap in the top right hand corner
+      const coordinates =
+        coordinatesOutsideElement(mediaControlsOverlayPlayButton(video));
+      doubleTapAtCoordinates(coordinates[0] + 1, coordinates[1] + 1, 400);
+    }
   });
 
+  video.addEventListener('pause', t.step_func(() => {
+    didPause = true;
+  }), { once: true });
+
   video.play();
 });
 </script>
diff --git a/third_party/WebKit/LayoutTests/media/controls/modern/tap-to-hide-controls.html b/third_party/WebKit/LayoutTests/media/controls/modern/tap-to-hide-controls.html
deleted file mode 100644
index 3b50eca..0000000
--- a/third_party/WebKit/LayoutTests/media/controls/modern/tap-to-hide-controls.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>Test that the controls are hidden if they are tapped</title>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../media-controls.js"></script>
-<video controls width=400 src="../../content/60_sec_video.webm"></video>
-<script>
-async_test(t => {
-  // This number comes from MediaControlOverlayPlayButtonElement.cpp.
-  const doubleTapTimeoutMs = 301;
-  const video = document.querySelector('video');
-
-  video.addEventListener('playing', t.step_func(() => {
-    assert_true(isControlsPanelVisible(video));
-
-    // Single tap in the top right hand corner
-    const c = coordinatesOutsideElement(mediaControlsOverlayPlayButton(video));
-    singleTapAtCoordinates(c[0] + 1, c[1] + 1, t.step_func(() => {
-      // Wait for the tap to expire.
-      setTimeout(t.step_func_done(() => {
-        assert_false(isControlsPanelVisible(video));
-      }), doubleTapTimeoutMs);
-    }));
-  }), { once: true });
-
-  video.play();
-});
-</script>
-</html>i
diff --git a/third_party/WebKit/LayoutTests/media/media-controls.js b/third_party/WebKit/LayoutTests/media/media-controls.js
index d41010aa..ed9b82d 100644
--- a/third_party/WebKit/LayoutTests/media/media-controls.js
+++ b/third_party/WebKit/LayoutTests/media/media-controls.js
@@ -319,7 +319,7 @@
   ]);
 }
 
-function singleTapAtCoordinates(xPos, yPos, callback) {
+function singleTapAtCoordinates(xPos, yPos) {
   chrome.gpuBenchmarking.pointerActionSequence([
     {
       source: 'mouse',
@@ -328,7 +328,7 @@
         { name: 'pointerUp' }
       ]
     }
-  ], callback);
+  ]);
 }
 
 function enableDoubleTapToJumpForTest(t) {
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
index 4b5bf7ff..e4cb3e9 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
@@ -714,14 +714,6 @@
   panel_->MakeOpaque();
 }
 
-void MediaControlsImpl::MakeOpaqueFromPointerEvent() {
-  if (IsVisible())
-    return;
-
-  MakeOpaque();
-  pointer_event_did_show_controls_ = true;
-}
-
 void MediaControlsImpl::MakeTransparent() {
   // Only hide the cursor if the controls are enabled.
   if (MediaElement().ShouldShowControls())
@@ -1002,28 +994,6 @@
     download_iph_manager_->UpdateInProductHelp();
 }
 
-void MediaControlsImpl::MaybeToggleControlsFromTap() {
-  if (MediaElement().paused())
-    return;
-
-  // If the controls are visible we should try to hide them unless they should
-  // be kept around for another reason. If the controls are not visible then
-  // show them and start the timer to automatically hide them. If a pointer
-  // event showed the controls in this batch of events then we should not hiden
-  // the controls.
-  if (IsVisible() && !pointer_event_did_show_controls_) {
-    MakeTransparent();
-  } else {
-    MakeOpaque();
-    if (ShouldHideMediaControls(kIgnoreWaitForTimer)) {
-      keep_showing_until_timer_fires_ = true;
-      StartHideMediaControlsTimer();
-    }
-
-    pointer_event_did_show_controls_ = false;
-  }
-}
-
 void MediaControlsImpl::DefaultEventHandler(Event* event) {
   HTMLDivElement::DefaultEventHandler(event);
 
@@ -1045,7 +1015,7 @@
   // Touch events are treated differently to avoid fake mouse events to trigger
   // random behavior. The expect behaviour for touch is that a tap will show the
   // controls and they will hide when the timer to hide fires.
-  if (is_touch_event && !IsModern()) {
+  if (is_touch_event) {
     if (event->type() != EventTypeNames::gesturetap)
       return;
 
@@ -1071,7 +1041,7 @@
     if (!ContainsRelatedTarget(event)) {
       is_mouse_over_controls_ = true;
       if (!MediaElement().paused()) {
-        MakeOpaqueFromPointerEvent();
+        MakeOpaque();
         StartHideMediaControlsIfNecessary();
       }
     }
@@ -1086,21 +1056,10 @@
     return;
   }
 
-  if (event->type() == EventTypeNames::click) {
-    MaybeToggleControlsFromTap();
-    return;
-  }
-
-  // The pointer event has finished so we should clear the bit.
-  if (event->type() == EventTypeNames::mouseout) {
-    pointer_event_did_show_controls_ = false;
-    return;
-  }
-
   if (event->type() == EventTypeNames::pointermove) {
     // When we get a mouse move, show the media controls, and start a timer
     // that will hide the media controls after a 3 seconds without a mouse move.
-    MakeOpaqueFromPointerEvent();
+    MakeOpaque();
     if (ShouldHideMediaControls(kIgnoreVideoHover))
       StartHideMediaControlsTimer();
     return;
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
index ee003f40..9b11473 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
@@ -157,8 +157,6 @@
   };
   ControlsState State() const;
 
-  void MaybeToggleControlsFromTap();
-
  private:
   // MediaControlsMediaEventListener is a component that is listening to events
   // and calling the appropriate callback on MediaControlsImpl. The object is
@@ -200,7 +198,6 @@
   void InitializeControls();
 
   void MakeOpaque();
-  void MakeOpaqueFromPointerEvent();
   void MakeTransparent();
   bool IsVisible() const;
 
@@ -319,8 +316,6 @@
 
   bool keep_showing_until_timer_fires_ : 1;
 
-  bool pointer_event_did_show_controls_ = false;
-
   Member<MediaDownloadInProductHelpManager> download_iph_manager_;
 };
 
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.cpp b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.cpp
index e479c0c..eaf4026 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.cpp
@@ -20,7 +20,6 @@
 #include "public/platform/Platform.h"
 #include "public/platform/TaskType.h"
 #include "public/platform/WebSize.h"
-#include "public/platform/WebTraceLocation.h"
 
 namespace {
 
@@ -36,8 +35,11 @@
           (y >= (rect.top() - margin)) && (y <= (rect.bottom() + margin)));
 }
 
-// The delay between two taps to be recognized as a double tap gesture.
-constexpr WTF::TimeDelta kDoubleTapDelay = TimeDelta::FromMilliseconds(300);
+// The delay if a touch is outside the internal button.
+constexpr WTF::TimeDelta kOutsideTouchDelay = TimeDelta::FromMilliseconds(300);
+
+// The delay if a touch is inside the internal button.
+constexpr WTF::TimeDelta kInsideTouchDelay = TimeDelta::FromMilliseconds(0);
 
 // The number of seconds to jump when double tapping.
 constexpr int kNumberOfSecondsToJump = 10;
@@ -183,13 +185,22 @@
     left_jump_arrow_->Show();
 }
 
+void MediaControlOverlayPlayButtonElement::HandlePlayPauseEvent(
+    Event* event,
+    WTF::TimeDelta delay) {
+  event->SetDefaultHandled();
+
+  if (tap_timer_.IsActive())
+    return;
+
+  tap_timer_.StartOneShot(delay, FROM_HERE);
+}
+
 void MediaControlOverlayPlayButtonElement::DefaultEventHandler(Event* event) {
   if (event->type() == EventTypeNames::click) {
-    event->SetDefaultHandled();
-
     // Double tap to navigate should only be available on modern controls.
     if (!MediaControlsImpl::IsModern() || !event->IsMouseEvent()) {
-      MaybePlayPause();
+      HandlePlayPauseEvent(event, kInsideTouchDelay);
       return;
     }
 
@@ -198,7 +209,7 @@
     // TODO(beccahughes): Move to PointerEvent.
     MouseEvent* mouse_event = ToMouseEvent(event);
     if (!mouse_event->HasPosition()) {
-      MaybePlayPause();
+      HandlePlayPauseEvent(event, kInsideTouchDelay);
       return;
     }
 
@@ -207,14 +218,12 @@
     if (IsPointInRect(*internal_button_->getBoundingClientRect(),
                       kInnerButtonTouchPaddingSize, mouse_event->clientX(),
                       mouse_event->clientY())) {
-      MaybePlayPause();
+      HandlePlayPauseEvent(event, kInsideTouchDelay);
     } else if (!tap_timer_.IsActive()) {
       // If there was not a previous touch and this was outside of the button
-      // then we should toggle visibility with a small unnoticeable delay in
-      // case their is a second tap.
-      if (tap_timer_.IsActive())
-        return;
-      tap_timer_.StartOneShot(kDoubleTapDelay, BLINK_FROM_HERE);
+      // then we should play/pause but with a small unnoticeable delay to allow
+      // for a secondary tap.
+      HandlePlayPauseEvent(event, kOutsideTouchDelay);
     } else {
       // Cancel the play pause event.
       tap_timer_.Stop();
@@ -256,7 +265,11 @@
 }
 
 void MediaControlOverlayPlayButtonElement::TapTimerFired(TimerBase*) {
-  GetMediaControls().MaybeToggleControlsFromTap();
+  std::unique_ptr<UserGestureIndicator> user_gesture_scope =
+      Frame::NotifyUserActivation(GetDocument().GetFrame(),
+                                  UserGestureToken::kNewGesture);
+
+  MaybePlayPause();
 }
 
 void MediaControlOverlayPlayButtonElement::Trace(blink::Visitor* visitor) {
diff --git a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.h b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.h
index 66a0e212..f5961a35 100644
--- a/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.h
+++ b/third_party/WebKit/Source/modules/media_controls/elements/MediaControlOverlayPlayButtonElement.h
@@ -78,6 +78,8 @@
   void MaybePlayPause();
   void MaybeJump(int);
 
+  void HandlePlayPauseEvent(Event*, WTF::TimeDelta);
+
   TaskRunnerTimer<MediaControlOverlayPlayButtonElement> tap_timer_;
 
   Member<HTMLDivElement> internal_button_;
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index e94b789..b364a52 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -21,7 +21,10 @@
   "graphics/cpu/arm/WebGLImageConversionNEON.h",
 ]
 
-blink_platform_msa_files = [ "graphics/cpu/mips/WebGLImageConversionMSA.h" ]
+blink_platform_msa_files = [
+  "audio/cpu/mips/VectorMathMSA.h",
+  "graphics/cpu/mips/WebGLImageConversionMSA.h",
+]
 
 blink_platform_sse_files = [
   "audio/cpu/x86/VectorMathSSE.h",
@@ -515,6 +518,7 @@
     "audio/VectorMathScalar.h",
     "audio/android/FFTFrameOpenMAXDLAndroid.cpp",
     "audio/cpu/arm/VectorMathNEON.h",
+    "audio/cpu/mips/VectorMathMSA.h",
     "audio/cpu/x86/VectorMathSSE.h",
     "audio/cpu/x86/VectorMathX86.h",
     "audio/ffmpeg/FFTFrameFFMPEG.cpp",
diff --git a/third_party/WebKit/Source/platform/audio/VectorMath.cpp b/third_party/WebKit/Source/platform/audio/VectorMath.cpp
index 2950177..1380a55 100644
--- a/third_party/WebKit/Source/platform/audio/VectorMath.cpp
+++ b/third_party/WebKit/Source/platform/audio/VectorMath.cpp
@@ -35,18 +35,14 @@
 #include "platform/audio/mac/VectorMathMac.h"
 #elif WTF_CPU_ARM_NEON
 #include "platform/audio/cpu/arm/VectorMathNEON.h"
+#elif HAVE_MIPS_MSA_INTRINSICS
+#include "platform/audio/cpu/mips/VectorMathMSA.h"
 #elif defined(ARCH_CPU_X86_FAMILY)
 #include "platform/audio/cpu/x86/VectorMathX86.h"
 #else
 #include "platform/audio/VectorMathScalar.h"
 #endif
 
-#if HAVE_MIPS_MSA_INTRINSICS
-#include "platform/cpu/mips/CommonMacrosMSA.h"
-#endif
-
-#include <algorithm>
-
 namespace blink {
 
 namespace VectorMath {
@@ -56,6 +52,8 @@
 namespace Impl = Mac;
 #elif WTF_CPU_ARM_NEON
 namespace Impl = NEON;
+#elif HAVE_MIPS_MSA_INTRINSICS
+namespace Impl = MSA;
 #elif defined(ARCH_CPU_X86_FAMILY)
 namespace Impl = X86;
 #else
@@ -63,77 +61,6 @@
 #endif
 }  // namespace
 
-void Vsma(const float* source_p,
-          int source_stride,
-          const float* scale,
-          float* dest_p,
-          int dest_stride,
-          size_t frames_to_process) {
-#if HAVE_MIPS_MSA_INTRINSICS
-  int n = frames_to_process;
-
-  if ((source_stride == 1) && (dest_stride == 1)) {
-    float* destPCopy = dest_p;
-    v4f32 vScale;
-    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
-    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
-    FloatInt scaleVal;
-
-    scaleVal.floatVal = *scale;
-    vScale = (v4f32)__msa_fill_w(scaleVal.intVal);
-
-    for (; n >= 32; n -= 32) {
-      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
-             vSrc7);
-      LD_SP8(destPCopy, 4, vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6,
-             vDst7);
-      VSMA4(vSrc0, vSrc1, vSrc2, vSrc3, vDst0, vDst1, vDst2, vDst3, vScale);
-      VSMA4(vSrc4, vSrc5, vSrc6, vSrc7, vDst4, vDst5, vDst6, vDst7, vScale);
-      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
-    }
-  }
-
-  frames_to_process = n;
-#endif
-
-  Impl::Vsma(source_p, source_stride, scale, dest_p, dest_stride,
-             frames_to_process);
-}
-
-void Vsmul(const float* source_p,
-           int source_stride,
-           const float* scale,
-           float* dest_p,
-           int dest_stride,
-           size_t frames_to_process) {
-#if HAVE_MIPS_MSA_INTRINSICS
-  int n = frames_to_process;
-
-  if ((source_stride == 1) && (dest_stride == 1)) {
-    v4f32 vScale;
-    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
-    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
-    FloatInt scaleVal;
-
-    scaleVal.floatVal = *scale;
-    vScale = (v4f32)__msa_fill_w(scaleVal.intVal);
-
-    for (; n >= 32; n -= 32) {
-      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
-             vSrc7);
-      VSMUL4(vSrc0, vSrc1, vSrc2, vSrc3, vDst0, vDst1, vDst2, vDst3, vScale);
-      VSMUL4(vSrc4, vSrc5, vSrc6, vSrc7, vDst4, vDst5, vDst6, vDst7, vScale);
-      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
-    }
-  }
-
-  frames_to_process = n;
-#endif
-
-  Impl::Vsmul(source_p, source_stride, scale, dest_p, dest_stride,
-              frames_to_process);
-}
-
 void Vadd(const float* source1p,
           int source_stride1,
           const float* source2p,
@@ -141,136 +68,10 @@
           float* dest_p,
           int dest_stride,
           size_t frames_to_process) {
-#if HAVE_MIPS_MSA_INTRINSICS
-  int n = frames_to_process;
-
-  if ((source_stride1 == 1) && (source_stride2 == 1) && (dest_stride == 1)) {
-    v4f32 vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5, vSrc1P6,
-        vSrc1P7;
-    v4f32 vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5, vSrc2P6,
-        vSrc2P7;
-    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
-
-    for (; n >= 32; n -= 32) {
-      LD_SP8(source1p, 4, vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5,
-             vSrc1P6, vSrc1P7);
-      LD_SP8(source2p, 4, vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5,
-             vSrc2P6, vSrc2P7);
-      ADD4(vSrc1P0, vSrc2P0, vSrc1P1, vSrc2P1, vSrc1P2, vSrc2P2, vSrc1P3,
-           vSrc2P3, vDst0, vDst1, vDst2, vDst3);
-      ADD4(vSrc1P4, vSrc2P4, vSrc1P5, vSrc2P5, vSrc1P6, vSrc2P6, vSrc1P7,
-           vSrc2P7, vDst4, vDst5, vDst6, vDst7);
-      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
-    }
-  }
-
-  frames_to_process = n;
-#endif
-
   Impl::Vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
              dest_stride, frames_to_process);
 }
 
-void Vmul(const float* source1p,
-          int source_stride1,
-          const float* source2p,
-          int source_stride2,
-          float* dest_p,
-          int dest_stride,
-          size_t frames_to_process) {
-#if HAVE_MIPS_MSA_INTRINSICS
-  int n = frames_to_process;
-
-  if ((source_stride1 == 1) && (source_stride2 == 1) && (dest_stride == 1)) {
-    v4f32 vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5, vSrc1P6,
-        vSrc1P7;
-    v4f32 vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5, vSrc2P6,
-        vSrc2P7;
-    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
-
-    for (; n >= 32; n -= 32) {
-      LD_SP8(source1p, 4, vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5,
-             vSrc1P6, vSrc1P7);
-      LD_SP8(source2p, 4, vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5,
-             vSrc2P6, vSrc2P7);
-      MUL4(vSrc1P0, vSrc2P0, vSrc1P1, vSrc2P1, vSrc1P2, vSrc2P2, vSrc1P3,
-           vSrc2P3, vDst0, vDst1, vDst2, vDst3);
-      MUL4(vSrc1P4, vSrc2P4, vSrc1P5, vSrc2P5, vSrc1P6, vSrc2P6, vSrc1P7,
-           vSrc2P7, vDst4, vDst5, vDst6, vDst7);
-      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
-    }
-  }
-
-  frames_to_process = n;
-#endif
-
-  Impl::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
-             dest_stride, frames_to_process);
-}
-
-void Zvmul(const float* real1p,
-           const float* imag1p,
-           const float* real2p,
-           const float* imag2p,
-           float* real_dest_p,
-           float* imag_dest_p,
-           size_t frames_to_process) {
-  Impl::Zvmul(real1p, imag1p, real2p, imag2p, real_dest_p, imag_dest_p,
-              frames_to_process);
-}
-
-void Vsvesq(const float* source_p,
-            int source_stride,
-            float* sum_p,
-            size_t frames_to_process) {
-  float sum = 0;
-
-  Impl::Vsvesq(source_p, source_stride, &sum, frames_to_process);
-
-  DCHECK(sum_p);
-  *sum_p = sum;
-}
-
-void Vmaxmgv(const float* source_p,
-             int source_stride,
-             float* max_p,
-             size_t frames_to_process) {
-  float max = 0;
-
-#if HAVE_MIPS_MSA_INTRINSICS
-  int n = frames_to_process;
-
-  if (source_stride == 1) {
-    v4f32 vMax = {
-        0,
-    };
-    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
-    const v16i8 vSignBitMask = (v16i8)__msa_fill_w(0x7FFFFFFF);
-
-    for (; n >= 32; n -= 32) {
-      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
-             vSrc7);
-      AND_W4_SP(vSrc0, vSrc1, vSrc2, vSrc3, vSignBitMask);
-      VMAX_W4_SP(vSrc0, vSrc1, vSrc2, vSrc3, vMax);
-      AND_W4_SP(vSrc4, vSrc5, vSrc6, vSrc7, vSignBitMask);
-      VMAX_W4_SP(vSrc4, vSrc5, vSrc6, vSrc7, vMax);
-    }
-
-    max = std::max(max, vMax[0]);
-    max = std::max(max, vMax[1]);
-    max = std::max(max, vMax[2]);
-    max = std::max(max, vMax[3]);
-  }
-
-  frames_to_process = n;
-#endif
-
-  Impl::Vmaxmgv(source_p, source_stride, &max, frames_to_process);
-
-  DCHECK(max_p);
-  *max_p = max;
-}
-
 void Vclip(const float* source_p,
            int source_stride,
            const float* low_threshold_p,
@@ -290,38 +91,80 @@
   DCHECK_LE(low_threshold, high_threshold);
 #endif
 
-#if HAVE_MIPS_MSA_INTRINSICS
-  int n = frames_to_process;
-
-  if ((source_stride == 1) && (dest_stride == 1)) {
-    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
-    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
-    v4f32 vLowThr, vHighThr;
-    FloatInt lowThr, highThr;
-
-    lowThr.floatVal = low_threshold;
-    highThr.floatVal = high_threshold;
-    vLowThr = (v4f32)__msa_fill_w(lowThr.intVal);
-    vHighThr = (v4f32)__msa_fill_w(highThr.intVal);
-
-    for (; n >= 32; n -= 32) {
-      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
-             vSrc7);
-      VCLIP4(vSrc0, vSrc1, vSrc2, vSrc3, vLowThr, vHighThr, vDst0, vDst1, vDst2,
-             vDst3);
-      VCLIP4(vSrc4, vSrc5, vSrc6, vSrc7, vLowThr, vHighThr, vDst4, vDst5, vDst6,
-             vDst7);
-      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
-    }
-  }
-
-  frames_to_process = n;
-#endif
-
   Impl::Vclip(source_p, source_stride, &low_threshold, &high_threshold, dest_p,
               dest_stride, frames_to_process);
 }
 
+void Vmaxmgv(const float* source_p,
+             int source_stride,
+             float* max_p,
+             size_t frames_to_process) {
+  float max = 0;
+
+  Impl::Vmaxmgv(source_p, source_stride, &max, frames_to_process);
+
+  DCHECK(max_p);
+  *max_p = max;
+}
+
+void Vmul(const float* source1p,
+          int source_stride1,
+          const float* source2p,
+          int source_stride2,
+          float* dest_p,
+          int dest_stride,
+          size_t frames_to_process) {
+  Impl::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+             dest_stride, frames_to_process);
+}
+
+void Vsma(const float* source_p,
+          int source_stride,
+          const float* scale,
+          float* dest_p,
+          int dest_stride,
+          size_t frames_to_process) {
+  const float k = *scale;
+
+  Impl::Vsma(source_p, source_stride, &k, dest_p, dest_stride,
+             frames_to_process);
+}
+
+void Vsmul(const float* source_p,
+           int source_stride,
+           const float* scale,
+           float* dest_p,
+           int dest_stride,
+           size_t frames_to_process) {
+  const float k = *scale;
+
+  Impl::Vsmul(source_p, source_stride, &k, dest_p, dest_stride,
+              frames_to_process);
+}
+
+void Vsvesq(const float* source_p,
+            int source_stride,
+            float* sum_p,
+            size_t frames_to_process) {
+  float sum = 0;
+
+  Impl::Vsvesq(source_p, source_stride, &sum, frames_to_process);
+
+  DCHECK(sum_p);
+  *sum_p = sum;
+}
+
+void Zvmul(const float* real1p,
+           const float* imag1p,
+           const float* real2p,
+           const float* imag2p,
+           float* real_dest_p,
+           float* imag_dest_p,
+           size_t frames_to_process) {
+  Impl::Zvmul(real1p, imag1p, real2p, imag2p, real_dest_p, imag_dest_p,
+              frames_to_process);
+}
+
 }  // namespace VectorMath
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/audio/cpu/mips/VectorMathMSA.h b/third_party/WebKit/Source/platform/audio/cpu/mips/VectorMathMSA.h
new file mode 100644
index 0000000..5754575c
--- /dev/null
+++ b/third_party/WebKit/Source/platform/audio/cpu/mips/VectorMathMSA.h
@@ -0,0 +1,232 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VectorMathMSA_h
+#define VectorMathMSA_h
+
+#include <algorithm>
+
+#include "platform/audio/VectorMathScalar.h"
+#include "platform/cpu/mips/CommonMacrosMSA.h"
+
+namespace blink {
+namespace VectorMath {
+namespace MSA {
+
+static ALWAYS_INLINE void Vadd(const float* source1p,
+                               int source_stride1,
+                               const float* source2p,
+                               int source_stride2,
+                               float* dest_p,
+                               int dest_stride,
+                               size_t frames_to_process) {
+  int n = frames_to_process;
+
+  if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+    v4f32 vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5, vSrc1P6,
+        vSrc1P7;
+    v4f32 vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5, vSrc2P6,
+        vSrc2P7;
+    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+
+    for (; n >= 32; n -= 32) {
+      LD_SP8(source1p, 4, vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5,
+             vSrc1P6, vSrc1P7);
+      LD_SP8(source2p, 4, vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5,
+             vSrc2P6, vSrc2P7);
+      ADD4(vSrc1P0, vSrc2P0, vSrc1P1, vSrc2P1, vSrc1P2, vSrc2P2, vSrc1P3,
+           vSrc2P3, vDst0, vDst1, vDst2, vDst3);
+      ADD4(vSrc1P4, vSrc2P4, vSrc1P5, vSrc2P5, vSrc1P6, vSrc2P6, vSrc1P7,
+           vSrc2P7, vDst4, vDst5, vDst6, vDst7);
+      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+    }
+  }
+
+  Scalar::Vadd(source1p, source_stride1, source2p, source_stride2, dest_p,
+               dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vclip(const float* source_p,
+                                int source_stride,
+                                const float* low_threshold_p,
+                                const float* high_threshold_p,
+                                float* dest_p,
+                                int dest_stride,
+                                size_t frames_to_process) {
+  int n = frames_to_process;
+
+  if (source_stride == 1 && dest_stride == 1) {
+    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+    v4f32 vLowThr, vHighThr;
+    FloatInt lowThr, highThr;
+
+    lowThr.floatVal = *low_threshold_p;
+    highThr.floatVal = *high_threshold_p;
+    vLowThr = (v4f32)__msa_fill_w(lowThr.intVal);
+    vHighThr = (v4f32)__msa_fill_w(highThr.intVal);
+
+    for (; n >= 32; n -= 32) {
+      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+             vSrc7);
+      VCLIP4(vSrc0, vSrc1, vSrc2, vSrc3, vLowThr, vHighThr, vDst0, vDst1, vDst2,
+             vDst3);
+      VCLIP4(vSrc4, vSrc5, vSrc6, vSrc7, vLowThr, vHighThr, vDst4, vDst5, vDst6,
+             vDst7);
+      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+    }
+  }
+
+  Scalar::Vclip(source_p, source_stride, low_threshold_p, high_threshold_p,
+                dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vmaxmgv(const float* source_p,
+                                  int source_stride,
+                                  float* max_p,
+                                  size_t frames_to_process) {
+  int n = frames_to_process;
+
+  if (source_stride == 1) {
+    v4f32 vMax = {
+        0,
+    };
+    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+    const v16i8 vSignBitMask = (v16i8)__msa_fill_w(0x7FFFFFFF);
+
+    for (; n >= 32; n -= 32) {
+      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+             vSrc7);
+      AND_W4_SP(vSrc0, vSrc1, vSrc2, vSrc3, vSignBitMask);
+      VMAX_W4_SP(vSrc0, vSrc1, vSrc2, vSrc3, vMax);
+      AND_W4_SP(vSrc4, vSrc5, vSrc6, vSrc7, vSignBitMask);
+      VMAX_W4_SP(vSrc4, vSrc5, vSrc6, vSrc7, vMax);
+    }
+
+    *max_p = std::max(*max_p, vMax[0]);
+    *max_p = std::max(*max_p, vMax[1]);
+    *max_p = std::max(*max_p, vMax[2]);
+    *max_p = std::max(*max_p, vMax[3]);
+  }
+
+  Scalar::Vmaxmgv(source_p, source_stride, max_p, n);
+}
+
+static ALWAYS_INLINE void Vmul(const float* source1p,
+                               int source_stride1,
+                               const float* source2p,
+                               int source_stride2,
+                               float* dest_p,
+                               int dest_stride,
+                               size_t frames_to_process) {
+  int n = frames_to_process;
+
+  if (source_stride1 == 1 && source_stride2 == 1 && dest_stride == 1) {
+    v4f32 vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5, vSrc1P6,
+        vSrc1P7;
+    v4f32 vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5, vSrc2P6,
+        vSrc2P7;
+    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+
+    for (; n >= 32; n -= 32) {
+      LD_SP8(source1p, 4, vSrc1P0, vSrc1P1, vSrc1P2, vSrc1P3, vSrc1P4, vSrc1P5,
+             vSrc1P6, vSrc1P7);
+      LD_SP8(source2p, 4, vSrc2P0, vSrc2P1, vSrc2P2, vSrc2P3, vSrc2P4, vSrc2P5,
+             vSrc2P6, vSrc2P7);
+      MUL4(vSrc1P0, vSrc2P0, vSrc1P1, vSrc2P1, vSrc1P2, vSrc2P2, vSrc1P3,
+           vSrc2P3, vDst0, vDst1, vDst2, vDst3);
+      MUL4(vSrc1P4, vSrc2P4, vSrc1P5, vSrc2P5, vSrc1P6, vSrc2P6, vSrc1P7,
+           vSrc2P7, vDst4, vDst5, vDst6, vDst7);
+      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+    }
+  }
+
+  Scalar::Vmul(source1p, source_stride1, source2p, source_stride2, dest_p,
+               dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsma(const float* source_p,
+                               int source_stride,
+                               const float* scale,
+                               float* dest_p,
+                               int dest_stride,
+                               size_t frames_to_process) {
+  int n = frames_to_process;
+
+  if (source_stride == 1 && dest_stride == 1) {
+    float* destPCopy = dest_p;
+    v4f32 vScale;
+    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+    FloatInt scaleVal;
+
+    scaleVal.floatVal = *scale;
+    vScale = (v4f32)__msa_fill_w(scaleVal.intVal);
+
+    for (; n >= 32; n -= 32) {
+      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+             vSrc7);
+      LD_SP8(destPCopy, 4, vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6,
+             vDst7);
+      VSMA4(vSrc0, vSrc1, vSrc2, vSrc3, vDst0, vDst1, vDst2, vDst3, vScale);
+      VSMA4(vSrc4, vSrc5, vSrc6, vSrc7, vDst4, vDst5, vDst6, vDst7, vScale);
+      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+    }
+  }
+
+  Scalar::Vsma(source_p, source_stride, scale, dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsmul(const float* source_p,
+                                int source_stride,
+                                const float* scale,
+                                float* dest_p,
+                                int dest_stride,
+                                size_t frames_to_process) {
+  int n = frames_to_process;
+
+  if (source_stride == 1 && dest_stride == 1) {
+    v4f32 vScale;
+    v4f32 vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6, vSrc7;
+    v4f32 vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7;
+    FloatInt scaleVal;
+
+    scaleVal.floatVal = *scale;
+    vScale = (v4f32)__msa_fill_w(scaleVal.intVal);
+
+    for (; n >= 32; n -= 32) {
+      LD_SP8(source_p, 4, vSrc0, vSrc1, vSrc2, vSrc3, vSrc4, vSrc5, vSrc6,
+             vSrc7);
+      VSMUL4(vSrc0, vSrc1, vSrc2, vSrc3, vDst0, vDst1, vDst2, vDst3, vScale);
+      VSMUL4(vSrc4, vSrc5, vSrc6, vSrc7, vDst4, vDst5, vDst6, vDst7, vScale);
+      ST_SP8(vDst0, vDst1, vDst2, vDst3, vDst4, vDst5, vDst6, vDst7, dest_p, 4);
+    }
+  }
+
+  Scalar::Vsmul(source_p, source_stride, scale, dest_p, dest_stride, n);
+}
+
+static ALWAYS_INLINE void Vsvesq(const float* source_p,
+                                 int source_stride,
+                                 float* sum_p,
+                                 size_t frames_to_process) {
+  Scalar::Vsvesq(source_p, source_stride, sum_p, n);
+}
+
+static ALWAYS_INLINE void Zvmul(const float* real1p,
+                                const float* imag1p,
+                                const float* real2p,
+                                const float* imag2p,
+                                float* real_dest_p,
+                                float* imag_dest_p,
+                                size_t frames_to_process) {
+  Scalar::Zvmul(real1p, imag1p, real2p, imag2p, real_dest_p, imag_dest_p,
+                frames_to_process);
+}
+
+}  // namespace MSA
+}  // namespace VectorMath
+}  // namespace blink
+
+#endif  // VectorMathMSA_h