[MediaControls] Non-touch Prototype

Media controls' prototype for non-touch devices.

Bug: 928443
Change-Id: I49ba8007a3ee7e99245a698e1579ac1988a4f2d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1504213
Reviewed-by: Mounir Lamouri <mlamouri@chromium.org>
Reviewed-by: Tommy Steimel <steimel@chromium.org>
Commit-Queue: Jazz Xu <jazzhsu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#640525}
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 26ed9a0d..9cd46b7 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -293,6 +293,7 @@
     "media_controls/media_controls_impl_test.cc",
     "media_controls/media_controls_orientation_lock_delegate_test.cc",
     "media_controls/media_controls_rotate_to_fullscreen_delegate_test.cc",
+    "media_controls/non_touch/media_controls_non_touch_impl_test.cc",
     "mediastream/media_constraints_test.cc",
     "mediastream/media_devices_test.cc",
     "notifications/notification_data_test.cc",
diff --git a/third_party/blink/renderer/modules/media_controls/BUILD.gn b/third_party/blink/renderer/modules/media_controls/BUILD.gn
index 680021e..f95ad65 100644
--- a/third_party/blink/renderer/modules/media_controls/BUILD.gn
+++ b/third_party/blink/renderer/modules/media_controls/BUILD.gn
@@ -88,6 +88,11 @@
     "media_controls_resource_loader.h",
     "media_controls_rotate_to_fullscreen_delegate.cc",
     "media_controls_rotate_to_fullscreen_delegate.h",
+    "non_touch/media_controls_non_touch_impl.cc",
+    "non_touch/media_controls_non_touch_impl.h",
+    "non_touch/media_controls_non_touch_media_event_listener.cc",
+    "non_touch/media_controls_non_touch_media_event_listener.h",
+    "non_touch/media_controls_non_touch_media_event_listener_observer.h",
   ]
 
   deps = [
diff --git a/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.cc b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.cc
new file mode 100644
index 0000000..6d4782523
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.h"
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/shadow_root.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/html/media/html_media_element.h"
+#include "third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.h"
+#include "third_party/blink/renderer/platform/keyboard_codes.h"
+
+namespace blink {
+
+MediaControlsNonTouchImpl::MediaControlsNonTouchImpl(
+    HTMLMediaElement& media_element)
+    : HTMLDivElement(media_element.GetDocument()),
+      MediaControls(media_element),
+      media_event_listener_(
+          MakeGarbageCollected<MediaControlsNonTouchMediaEventListener>(
+              media_element)) {
+  SetShadowPseudoId(AtomicString("-internal-media-controls-non-touch"));
+  media_event_listener_->AddObserver(this);
+}
+
+MediaControlsNonTouchImpl* MediaControlsNonTouchImpl::Create(
+    HTMLMediaElement& media_element,
+    ShadowRoot& shadow_root) {
+  MediaControlsNonTouchImpl* controls =
+      MakeGarbageCollected<MediaControlsNonTouchImpl>(media_element);
+  shadow_root.ParserAppendChild(controls);
+  return controls;
+}
+
+Node::InsertionNotificationRequest MediaControlsNonTouchImpl::InsertedInto(
+    ContainerNode& root) {
+  media_event_listener_->Attach();
+  return HTMLDivElement::InsertedInto(root);
+}
+
+void MediaControlsNonTouchImpl::RemovedFrom(ContainerNode& insertion_point) {
+  HTMLDivElement::RemovedFrom(insertion_point);
+  Hide();
+  media_event_listener_->Detach();
+}
+
+void MediaControlsNonTouchImpl::MaybeShow() {
+  // show controls
+}
+
+void MediaControlsNonTouchImpl::Hide() {
+  // hide controls
+}
+
+void MediaControlsNonTouchImpl::OnFocusIn() {
+  if (MediaElement().ShouldShowControls())
+    MaybeShow();
+}
+
+void MediaControlsNonTouchImpl::OnKeyDown(KeyboardEvent* event) {
+  bool handled = true;
+  switch (event->keyCode()) {
+    case VKEY_RETURN:
+      MediaElement().TogglePlayState();
+      break;
+    case VKEY_LEFT:
+    case VKEY_RIGHT:
+    case VKEY_UP:
+    case VKEY_DOWN:
+      // do something
+      break;
+    default:
+      handled = false;
+      break;
+  }
+
+  if (handled)
+    event->SetDefaultHandled();
+}
+
+void MediaControlsNonTouchImpl::Trace(blink::Visitor* visitor) {
+  visitor->Trace(media_event_listener_);
+  MediaControls::Trace(visitor);
+  HTMLDivElement::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.h b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.h
new file mode 100644
index 0000000..cb278933
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.h
@@ -0,0 +1,72 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_IMPL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_IMPL_H_
+
+#include "third_party/blink/renderer/core/html/html_div_element.h"
+#include "third_party/blink/renderer/core/html/media/media_controls.h"
+#include "third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+
+namespace blink {
+
+class MediaControlsNonTouchMediaEventListener;
+
+class MODULES_EXPORT MediaControlsNonTouchImpl final
+    : public HTMLDivElement,
+      public MediaControls,
+      public MediaControlsNonTouchMediaEventListenerObserver {
+  USING_GARBAGE_COLLECTED_MIXIN(MediaControlsNonTouchImpl);
+
+ public:
+  static MediaControlsNonTouchImpl* Create(HTMLMediaElement&, ShadowRoot&);
+
+  explicit MediaControlsNonTouchImpl(HTMLMediaElement&);
+
+  // Node override.
+  Node::InsertionNotificationRequest InsertedInto(ContainerNode&) override;
+  void RemovedFrom(ContainerNode&) override;
+
+  // MediaControls implementation.
+  void MaybeShow() override;
+  void Hide() override;
+  void Reset() override {}
+  void OnControlsListUpdated() override {}
+  void OnTrackElementFailedToLoad() override {}
+  void NetworkStateChanged() override {}
+  LayoutObject* PanelLayoutObject() override { return nullptr; }
+  LayoutObject* TimelineLayoutObject() override { return nullptr; }
+  LayoutObject* ButtonPanelLayoutObject() override { return nullptr; }
+  LayoutObject* ContainerLayoutObject() override { return nullptr; }
+  void SetTestMode(bool) override {}
+  HTMLDivElement* PanelElement() override { return nullptr; }
+  void OnMediaControlsEnabledChange() override {}
+
+  // MediaControlsNonTouchMediaEventListenerObserver implementation.
+  void OnFocusIn() override;
+  void OnTimeUpdate() override {}
+  void OnDurationChange() override {}
+  void OnPlay() override {}
+  void OnPause() override {}
+  void OnError() override {}
+  void OnLoadedMetadata() override {}
+  void OnKeyPress(KeyboardEvent* event) override {}
+  void OnKeyDown(KeyboardEvent* event) override;
+  void OnKeyUp(KeyboardEvent* event) override {}
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  friend class MediaControlsNonTouchImplTest;
+
+  // Node
+  bool IsMediaControls() const override { return true; }
+
+  Member<MediaControlsNonTouchMediaEventListener> media_event_listener_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_IMPL_H_
diff --git a/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl_test.cc b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl_test.cc
new file mode 100644
index 0000000..dbc8b42d
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl_test.cc
@@ -0,0 +1,82 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_impl.h"
+
+#include <memory>
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/html/media/html_media_element.h"
+#include "third_party/blink/renderer/core/html/media/html_media_test_helper.h"
+#include "third_party/blink/renderer/core/html/media/html_video_element.h"
+#include "third_party/blink/renderer/core/loader/empty_clients.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/keyboard_codes.h"
+#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
+
+namespace blink {
+
+namespace {
+
+class MockWebMediaPlayerForNonTouchImpl : public EmptyWebMediaPlayer {
+ public:
+  bool HasVideo() const override { return true; }
+};
+
+class MediaControlsNonTouchImplTest : public PageTestBase {
+ protected:
+  void SetUp() override { InitializePage(); }
+
+  void InitializePage() {
+    Page::PageClients clients;
+    FillWithEmptyClients(clients);
+    clients.chrome_client = MakeGarbageCollected<EmptyChromeClient>();
+    SetupPageWithClients(
+        &clients, test::MediaStubLocalFrameClient::Create(
+                      std::make_unique<MockWebMediaPlayerForNonTouchImpl>()));
+
+    GetDocument().write("<video>");
+    HTMLMediaElement& video =
+        ToHTMLVideoElement(*GetDocument().QuerySelector("video"));
+    media_controls_ = MediaControlsNonTouchImpl::Create(
+        video, video.EnsureUserAgentShadowRoot());
+  }
+
+  MediaControlsNonTouchImpl& MediaControls() { return *media_controls_; }
+
+  void SimulateKeydownEvent(Element& element, int key_code) {
+    KeyboardEventInit* keyboard_event_init = KeyboardEventInit::Create();
+    keyboard_event_init->setKeyCode(key_code);
+
+    Event* keyboard_event =
+        MakeGarbageCollected<KeyboardEvent>("keydown", keyboard_event_init);
+    element.DispatchEvent(*keyboard_event);
+  }
+
+ private:
+  Persistent<MediaControlsNonTouchImpl> media_controls_;
+};
+
+TEST_F(MediaControlsNonTouchImplTest, PlayPause) {
+  MediaControls().MediaElement().SetFocused(true,
+                                            WebFocusType::kWebFocusTypeNone);
+  MediaControls().MediaElement().Play();
+  ASSERT_FALSE(MediaControls().MediaElement().paused());
+
+  // Press center key and video should be paused.
+  SimulateKeydownEvent(MediaControls().MediaElement(), VKEY_RETURN);
+  ASSERT_TRUE(MediaControls().MediaElement().paused());
+
+  // Press center key and video should be played.
+  SimulateKeydownEvent(MediaControls().MediaElement(), VKEY_RETURN);
+  ASSERT_FALSE(MediaControls().MediaElement().paused());
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.cc b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.cc
new file mode 100644
index 0000000..2ee0746
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.cc
@@ -0,0 +1,116 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.h"
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/html/media/html_media_element.h"
+#include "third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener_observer.h"
+
+namespace blink {
+
+MediaControlsNonTouchMediaEventListener::
+    MediaControlsNonTouchMediaEventListener(HTMLMediaElement& media_element)
+    : media_element_(media_element) {
+  if (media_element.isConnected())
+    Attach();
+}
+
+void MediaControlsNonTouchMediaEventListener::AddObserver(
+    MediaControlsNonTouchMediaEventListenerObserver* observer) {
+  observers_.insert(observer);
+}
+
+void MediaControlsNonTouchMediaEventListener::RemoveObserver(
+    MediaControlsNonTouchMediaEventListenerObserver* observer) {
+  observers_.erase(observer);
+}
+
+void MediaControlsNonTouchMediaEventListener::Attach() {
+  DCHECK(media_element_->isConnected());
+
+  media_element_->addEventListener(event_type_names::kFocusin, this, false);
+  media_element_->addEventListener(event_type_names::kTimeupdate, this, false);
+  media_element_->addEventListener(event_type_names::kDurationchange, this,
+                                   false);
+
+  media_element_->addEventListener(event_type_names::kPlay, this, false);
+  media_element_->addEventListener(event_type_names::kPause, this, false);
+
+  media_element_->addEventListener(event_type_names::kError, this, false);
+  media_element_->addEventListener(event_type_names::kLoadedmetadata, this,
+                                   false);
+
+  media_element_->addEventListener(event_type_names::kKeypress, this, false);
+  media_element_->addEventListener(event_type_names::kKeydown, this, false);
+  media_element_->addEventListener(event_type_names::kKeyup, this, false);
+}
+
+void MediaControlsNonTouchMediaEventListener::Detach() {
+  DCHECK(!media_element_->isConnected());
+}
+
+void MediaControlsNonTouchMediaEventListener::Invoke(
+    ExecutionContext* execution_context,
+    Event* event) {
+  if (event->type() == event_type_names::kFocusin) {
+    for (auto& observer : observers_)
+      observer->OnFocusIn();
+    return;
+  }
+  if (event->type() == event_type_names::kTimeupdate) {
+    for (auto& observer : observers_)
+      observer->OnTimeUpdate();
+    return;
+  }
+  if (event->type() == event_type_names::kDurationchange) {
+    for (auto& observer : observers_)
+      observer->OnDurationChange();
+    return;
+  }
+  if (event->type() == event_type_names::kPlay) {
+    for (auto& observer : observers_)
+      observer->OnPlay();
+    return;
+  }
+  if (event->type() == event_type_names::kPause) {
+    for (auto& observer : observers_)
+      observer->OnPause();
+    return;
+  }
+  if (event->type() == event_type_names::kError) {
+    for (auto& observer : observers_)
+      observer->OnError();
+    return;
+  }
+  if (event->type() == event_type_names::kLoadedmetadata) {
+    for (auto& observer : observers_)
+      observer->OnLoadedMetadata();
+    return;
+  }
+  if (event->type() == event_type_names::kKeypress) {
+    for (auto& observer : observers_)
+      observer->OnKeyPress(ToKeyboardEvent(event));
+    return;
+  }
+  if (event->type() == event_type_names::kKeydown) {
+    for (auto& observer : observers_)
+      observer->OnKeyDown(ToKeyboardEvent(event));
+    return;
+  }
+  if (event->type() == event_type_names::kKeyup) {
+    for (auto& observer : observers_)
+      observer->OnKeyUp(ToKeyboardEvent(event));
+    return;
+  }
+}
+
+void MediaControlsNonTouchMediaEventListener::Trace(blink::Visitor* visitor) {
+  NativeEventListener::Trace(visitor);
+  visitor->Trace(media_element_);
+  visitor->Trace(observers_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.h b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.h
new file mode 100644
index 0000000..39329a67
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener.h
@@ -0,0 +1,41 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_MEDIA_EVENT_LISTENER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_MEDIA_EVENT_LISTENER_H_
+
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
+
+namespace blink {
+
+class HTMLMediaElement;
+class MediaControlsNonTouchMediaEventListenerObserver;
+
+class MediaControlsNonTouchMediaEventListener final
+    : public NativeEventListener {
+ public:
+  explicit MediaControlsNonTouchMediaEventListener(HTMLMediaElement&);
+
+  void AddObserver(MediaControlsNonTouchMediaEventListenerObserver*);
+  void RemoveObserver(MediaControlsNonTouchMediaEventListenerObserver*);
+
+  void Attach();
+  void Detach();
+
+  void Trace(blink::Visitor*) override;
+  void Invoke(ExecutionContext*, Event*) override;
+
+ private:
+  HTMLMediaElement& GetMediaElement();
+
+  HeapListHashSet<Member<MediaControlsNonTouchMediaEventListenerObserver>>
+      observers_;
+  Member<HTMLMediaElement> media_element_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaControlsNonTouchMediaEventListener);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_MEDIA_EVENT_LISTENER_H_
diff --git a/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener_observer.h b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener_observer.h
new file mode 100644
index 0000000..3d7d55b
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/non_touch/media_controls_non_touch_media_event_listener_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_MEDIA_EVENT_LISTENER_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_MEDIA_EVENT_LISTENER_OBSERVER_H_
+
+namespace blink {
+
+class KeyboardEvent;
+
+class MediaControlsNonTouchMediaEventListenerObserver
+    : public GarbageCollectedMixin {
+ public:
+  virtual void OnFocusIn() = 0;
+  virtual void OnTimeUpdate() = 0;
+  virtual void OnDurationChange() = 0;
+  virtual void OnPlay() = 0;
+  virtual void OnPause() = 0;
+  virtual void OnError() = 0;
+  virtual void OnLoadedMetadata() = 0;
+  virtual void OnKeyPress(KeyboardEvent* event) = 0;
+  virtual void OnKeyDown(KeyboardEvent* event) = 0;
+  virtual void OnKeyUp(KeyboardEvent* event) = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_NON_TOUCH_MEDIA_CONTROLS_NON_TOUCH_MEDIA_EVENT_LISTENER_OBSERVER_H_