Bug 887934 - Part 2 - Reposition closed caption according to video control. r?rillian draft
authorRay Lin <ralin@mozilla.com>
Mon, 30 May 2016 13:40:41 +0800
changeset 372697 0e819fb10074f79e48f80dfa867cfd7e86028c06
parent 372696 d6a304cbba3f0b436316df4258987070a22cf891
child 522220 8d28c1cae1de04a2b6f6df61577b67bf517d9768
push id19568
push userbmo:ralin@mozilla.com
push dateMon, 30 May 2016 05:49:18 +0000
reviewersrillian
bugs887934
milestone49.0a1
Bug 887934 - Part 2 - Reposition closed caption according to video control. r?rillian MozReview-Commit-ID: HsHowVwkitM
dom/html/TextTrackManager.cpp
dom/media/webvtt/WebVTTParserWrapper.js
dom/media/webvtt/nsIWebVTTParserWrapper.idl
dom/media/webvtt/vtt.jsm
layout/generic/nsVideoFrame.h
--- a/dom/html/TextTrackManager.cpp
+++ b/dom/html/TextTrackManager.cpp
@@ -202,16 +202,17 @@ TextTrackManager::UpdateCueDisplay()
 
   nsIFrame* frame = mMediaElement->GetPrimaryFrame();
   nsVideoFrame* videoFrame = do_QueryFrame(frame);
   if (!videoFrame) {
     return;
   }
 
   nsCOMPtr<nsIContent> overlay = videoFrame->GetCaptionOverlay();
+  nsCOMPtr<nsIContent> controls = videoFrame->GetVideoControls();
   if (!overlay) {
     return;
   }
 
   nsTArray<RefPtr<TextTrackCue> > activeCues;
   mTextTracks->UpdateAndGetShowingCues(activeCues);
 
   if (activeCues.Length() > 0) {
@@ -219,17 +220,17 @@ TextTrackManager::UpdateCueDisplay()
 
     jsCues->SetAsArray(nsIDataType::VTYPE_INTERFACE,
                        &NS_GET_IID(nsIDOMEventTarget),
                        activeCues.Length(),
                        static_cast<void*>(activeCues.Elements()));
 
     nsPIDOMWindowInner* window = mMediaElement->OwnerDoc()->GetInnerWindow();
     if (window) {
-      sParserWrapper->ProcessCues(window, jsCues, overlay);
+      sParserWrapper->ProcessCues(window, jsCues, overlay, controls);
     }
   } else if (overlay->Length() > 0) {
     nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true);
   }
 }
 
 void
 TextTrackManager::AddCue(TextTrackCue& aCue)
@@ -258,16 +259,20 @@ TextTrackManager::PopulatePendingList()
 }
 
 void
 TextTrackManager::AddListeners()
 {
   if (mMediaElement) {
     mMediaElement->AddEventListener(NS_LITERAL_STRING("resizevideocontrols"),
                                     this, false, false);
+    mMediaElement->AddEventListener(NS_LITERAL_STRING("controlbarchange"),
+                                    this, false, true);
+    mMediaElement->AddEventListener(NS_LITERAL_STRING("texttrackchange"),
+                                    this, false, true);
   }
 }
 
 void
 TextTrackManager::HonorUserPreferencesForTrackSelection()
 {
   if (performedTrackSelection || !mTextTracks) {
     return;
@@ -372,13 +377,26 @@ TextTrackManager::HandleEvent(nsIDOMEven
 
   nsAutoString type;
   aEvent->GetType(type);
   if (type.EqualsLiteral("resizevideocontrols")) {
     for (uint32_t i = 0; i< mTextTracks->Length(); i++) {
       ((*mTextTracks)[i])->SetCuesDirty();
     }
   }
+
+  if (type.EqualsLiteral("controlbarchange")) {
+    for (uint32_t i = 0; i< mTextTracks->Length(); i++) {
+      ((*mTextTracks)[i])->SetCuesDirty();
+    }
+
+    UpdateCueDisplay();
+  }
+
+  if (type.EqualsLiteral("texttrackchange")) {
+    UpdateCueDisplay();
+  }
+
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webvtt/WebVTTParserWrapper.js
+++ b/dom/media/webvtt/WebVTTParserWrapper.js
@@ -49,19 +49,19 @@ WebVTTParserWrapper.prototype =
     };
   },
 
   convertCueToDOMTree: function(window, cue)
   {
     return WebVTT.convertCueToDOMTree(window, cue.text);
   },
 
-  processCues: function(window, cues, overlay)
+  processCues: function(window, cues, overlay, controls)
   {
-    WebVTT.processCues(window, cues, overlay);
+    WebVTT.processCues(window, cues, overlay, controls);
   },
 
   classDescription: "Wrapper for the JS WebVTT implementation (vtt.js)",
   classID: Components.ID(WEBVTTPARSERWRAPPER_CID),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebVTTParserWrapper]),
   classInfo: XPCOMUtils.generateCI({
     classID:    WEBVTTPARSERWRAPPER_CID,
     contractID: WEBVTTPARSERWRAPPER_CONTRACTID,
--- a/dom/media/webvtt/nsIWebVTTParserWrapper.idl
+++ b/dom/media/webvtt/nsIWebVTTParserWrapper.idl
@@ -72,16 +72,17 @@ interface nsIWebVTTParserWrapper : nsISu
    * overlap avoidance using the dimensions of 'overlay'. Finally, it adds the
    * computed divs to the VTTCues display state property for use later.
    *
    * @param window  A window object with which it will create the DOM tree
    *                and containing div element.
    * @param cues    An array of VTTCues who need there display state to be
    *                computed.
    * @param overlay The HTMLElement that the cues will be displayed within.
+   * @param controls The XBL Element that may affect cues position.
    */
   void processCues(in mozIDOMWindow window, in nsIVariant cues,
-                   in nsISupports overlay);
+                   in nsISupports overlay, in nsISupports controls);
 };
 
 %{C++
 #define NS_WEBVTTPARSERWRAPPER_CONTRACTID "@mozilla.org/webvttParserWrapper;1"
 %}
--- a/dom/media/webvtt/vtt.jsm
+++ b/dom/media/webvtt/vtt.jsm
@@ -1143,26 +1143,37 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
 
   var FONT_SIZE_PERCENT = 0.05;
   var FONT_STYLE = "sans-serif";
   var CUE_BACKGROUND_PADDING = "1.5%";
 
   // Runs the processing model over the cues and regions passed to it.
   // @param overlay A block level element (usually a div) that the computed cues
   //                and regions will be placed into.
-  WebVTT.processCues = function(window, cues, overlay) {
+  // @param controls A XBL that controls playback. Cues' position will be
+  //                 affected and restyled according to it.
+  WebVTT.processCues = function(window, cues, overlay, controls) {
     if (!window || !cues || !overlay) {
       return null;
     }
 
     // Remove all previous children.
     while (overlay.firstChild) {
       overlay.removeChild(overlay.firstChild);
     }
 
+    var controlBar, cueOffsetY;
+
+    if (controls) {
+      controlBar = controls.ownerDocument.getAnonymousElementByAttribute(
+        controls, "class", "controlBar");
+    }
+
+    cueOffsetY = controlBar ? controlBar.clientHeight : 0;
+
     var paddedOverlay = window.document.createElement("div");
     paddedOverlay.style.position = "absolute";
     paddedOverlay.style.left = "0";
     paddedOverlay.style.right = "0";
     paddedOverlay.style.top = "0";
     paddedOverlay.style.bottom = "0";
     paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
     overlay.appendChild(paddedOverlay);
@@ -1202,16 +1213,21 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
 
         // Compute the intial position and styles of the cue div.
         styleBox = new CueStyleBox(window, cue, styleOptions);
         paddedOverlay.appendChild(styleBox.div);
 
         // Move the cue div to it's correct line position.
         moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
 
+        // Vertically move up only when cue overlapped with control bar
+        if (+styleBox.div.style.bottom.replace(/px$/, "") < cueOffsetY) {
+          styleBox.div.style.transform = `translateY(-${cueOffsetY}px)`;
+        }
+
         // Remember the computed div so that we don't have to recompute it later
         // if we don't have too.
         cue.displayState = styleBox.div;
 
         boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
       }
     })();
   };
--- a/layout/generic/nsVideoFrame.h
+++ b/layout/generic/nsVideoFrame.h
@@ -97,16 +97,18 @@ public:
   nsIContent* GetPosterImage() { return mPosterImage; }
 
   // Returns true if we should display the poster. Note that once we show
   // a video frame, the poster will never be displayed again.
   bool ShouldDisplayPoster();
 
   nsIContent *GetCaptionOverlay() { return mCaptionDiv; }
 
+  nsIContent *GetVideoControls() { return mVideoControls; }
+
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      nsDisplayItem* aItem,
                                      const ContainerLayerParameters& aContainerParameters);