Bug 1271765 - Part 1: Remove XUL specific reflow code of video control. r=dholbert draft
authorRay Lin <ralin@mozilla.com>
Wed, 12 Oct 2016 13:41:50 +0800
changeset 440738 b931d68b87a693e06310d6ee788afa300c2ccda4
parent 440454 05e5b12f41df270b31955ff7e6d09245c1f83a7a
child 440739 6e9d55baed71352d21c73ac00cccd32d140384ea
push id36312
push userbmo:ralin@mozilla.com
push dateFri, 18 Nov 2016 02:12:31 +0000
reviewersdholbert
bugs1271765
milestone53.0a1
Bug 1271765 - Part 1: Remove XUL specific reflow code of video control. r=dholbert MozReview-Commit-ID: KFn3ga2Uqq2
layout/generic/crashtests/1271765.html
layout/generic/crashtests/crashtests.list
layout/generic/nsVideoFrame.cpp
layout/style/res/html.css
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1271765.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<body>
+<div id="content" class="entry">
+  <audio controls style="writing-mode: vertical-lr"></audio>
+</div>
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -637,8 +637,9 @@ load large-border-radius-dashed.html
 load large-border-radius-dashed2.html
 load large-border-radius-dotted.html
 load large-border-radius-dotted2.html
 load 1297427-non-equal-centers.html
 load 1278461-1.html
 load 1278461-2.html
 load 1304441.html
 load 1316649.html
+skip-if(Android) load 1271765.html
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -293,28 +293,36 @@ nsVideoFrame::Reflow(nsPresContext* aPre
                  ("enter nsVideoFrame::Reflow: availSize=%d,%d",
                   aReflowInput.AvailableWidth(),
                   aReflowInput.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
   aStatus = NS_FRAME_COMPLETE;
 
-  aMetrics.Width() = aReflowInput.ComputedWidth();
-  aMetrics.Height() = aReflowInput.ComputedHeight();
+  const WritingMode myWM = aReflowInput.GetWritingMode();
+  nscoord contentBoxBSize = aReflowInput.ComputedBSize();
+  const nscoord borderBoxISize = aReflowInput.ComputedISize() +
+    aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM);
+  const bool isBSizeShrinkWrapping = (contentBoxBSize == NS_INTRINSICSIZE);
 
-  // stash this away so we can compute our inner area later
+  nscoord borderBoxBSize;
+  if (!isBSizeShrinkWrapping) {
+    borderBoxBSize = contentBoxBSize +
+      aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
+  }
+
   mBorderPadding   = aReflowInput.ComputedPhysicalBorderPadding();
 
-  aMetrics.Width() += mBorderPadding.left + mBorderPadding.right;
-  aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom;
+  // Reflow the child frames. We may have up to three: an image
+  // frame (for the poster image), a container frame for the controls,
+  // and a container frame for the caption.
+  for (nsIFrame* child : mFrames) {
+    nsSize oldChildSize = child->GetSize();
 
-  // Reflow the child frames. We may have up to two, an image frame
-  // which is the poster, and a box frame, which is the video controls.
-  for (nsIFrame* child : mFrames) {
     if (child->GetContent() == mPosterImage) {
       // Reflow the poster frame.
       nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
       ReflowOutput kidDesiredSize(aReflowInput);
       WritingMode wm = imageFrame->GetWritingMode();
       LogicalSize availableSize = aReflowInput.AvailableSize(wm);
       LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
                              ConvertTo(wm, aMetrics.GetWritingMode());
@@ -333,56 +341,77 @@ nsVideoFrame::Reflow(nsPresContext* aPre
       }
       kidReflowInput.SetComputedWidth(posterRenderRect.width);
       kidReflowInput.SetComputedHeight(posterRenderRect.height);
       ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput,
                   posterRenderRect.x, posterRenderRect.y, 0, aStatus);
       FinishReflowChild(imageFrame, aPresContext,
                         kidDesiredSize, &kidReflowInput,
                         posterRenderRect.x, posterRenderRect.y, 0);
+
+// Android still uses XUL media controls & hence needs this XUL-friendly
+// custom reflow code. This will go away in bug 1310907.
+#ifdef ANDROID
     } else if (child->GetContent() == mVideoControls) {
       // Reflow the video controls frame.
       nsBoxLayoutState boxState(PresContext(), aReflowInput.mRenderingContext);
-      nsSize size = child->GetSize();
       nsBoxFrame::LayoutChildAt(boxState,
                                 child,
                                 nsRect(mBorderPadding.left,
                                        mBorderPadding.top,
                                        aReflowInput.ComputedWidth(),
                                        aReflowInput.ComputedHeight()));
-      if (child->GetSize() != size) {
-        RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent());
-        nsContentUtils::AddScriptRunner(event);
-      }
-    } else if (child->GetContent() == mCaptionDiv) {
-      // Reflow to caption div
-      ReflowOutput kidDesiredSize(aReflowInput);
+
+#endif // ANDROID
+    } else if (child->GetContent() == mCaptionDiv ||
+               child->GetContent() == mVideoControls) {
+      // Reflow the caption and control bar frames.
       WritingMode wm = child->GetWritingMode();
-      LogicalSize availableSize = aReflowInput.AvailableSize(wm);
-      LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()).
-                             ConvertTo(wm, aMetrics.GetWritingMode());
+      LogicalSize availableSize = aReflowInput.ComputedSize(wm);
+      availableSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+
       ReflowInput kidReflowInput(aPresContext,
                                        aReflowInput,
                                        child,
-                                       availableSize,
-                                       &cbSize);
-      nsSize size(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
-      size.width -= kidReflowInput.ComputedPhysicalBorderPadding().LeftRight();
-      size.height -= kidReflowInput.ComputedPhysicalBorderPadding().TopBottom();
-
-      kidReflowInput.SetComputedWidth(std::max(size.width, 0));
-      kidReflowInput.SetComputedHeight(std::max(size.height, 0));
-
+                                       availableSize);
+      ReflowOutput kidDesiredSize(kidReflowInput);
       ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput,
                   mBorderPadding.left, mBorderPadding.top, 0, aStatus);
+
+      if (child->GetContent() == mVideoControls && isBSizeShrinkWrapping) {
+        contentBoxBSize = kidDesiredSize.BSize(myWM);
+      }
+
       FinishReflowChild(child, aPresContext,
                         kidDesiredSize, &kidReflowInput,
                         mBorderPadding.left, mBorderPadding.top, 0);
     }
+
+    if (child->GetContent() == mVideoControls && child->GetSize() != oldChildSize) {
+      RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent());
+      nsContentUtils::AddScriptRunner(event);
+    }
   }
+
+  if (isBSizeShrinkWrapping) {
+    if (contentBoxBSize == NS_INTRINSICSIZE) {
+      // We didn't get a BSize from our intrinsic size/ratio, nor did we
+      // get one from our controls. Just use BSize of 0.
+      contentBoxBSize = 0;
+    }
+    contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize,
+                                    aReflowInput.ComputedMinBSize(),
+                                    aReflowInput.ComputedMaxBSize());
+    borderBoxBSize = contentBoxBSize +
+      aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM);
+  }
+
+  LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize);
+  aMetrics.SetSize(myWM, logicalDesiredSize);
+
   aMetrics.SetOverflowAreasToDesiredBounds();
 
   FinishAndStoreOverflow(&aMetrics);
 
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                  ("exit nsVideoFrame::Reflow: size=%d,%d",
                   aMetrics.Width(), aMetrics.Height()));
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
@@ -521,16 +550,32 @@ nsVideoFrame::ComputeSize(nsRenderingCon
                           WritingMode aWM,
                           const LogicalSize& aCBSize,
                           nscoord aAvailableISize,
                           const LogicalSize& aMargin,
                           const LogicalSize& aBorder,
                           const LogicalSize& aPadding,
                           ComputeSizeFlags aFlags)
 {
+// When in no video scenario, it should fall back to inherited method.
+// We keep old codepath here since Android still uses XUL media controls.
+// This will go away in bug 1310907.
+#ifndef ANDROID
+  if (!HasVideoElement()) {
+    return nsContainerFrame::ComputeSize(aRenderingContext,
+                                         aWM,
+                                         aCBSize,
+                                         aAvailableISize,
+                                         aMargin,
+                                         aBorder,
+                                         aPadding,
+                                         aFlags);
+  }
+#endif // ANDROID
+
   nsSize size = GetVideoIntrinsicSize(aRenderingContext);
 
   IntrinsicSize intrinsicSize;
   intrinsicSize.width.SetCoordValue(size.width);
   intrinsicSize.height.SetCoordValue(size.height);
 
   // Only video elements have an intrinsic ratio.
   nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0);
@@ -538,27 +583,59 @@ nsVideoFrame::ComputeSize(nsRenderingCon
   return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
                                             intrinsicSize, intrinsicRatio,
                                             aCBSize, aMargin, aBorder, aPadding,
                                             aFlags);
 }
 
 nscoord nsVideoFrame::GetMinISize(nsRenderingContext *aRenderingContext)
 {
-  nsSize size = GetVideoIntrinsicSize(aRenderingContext);
-  nscoord result = GetWritingMode().IsVertical() ? size.height : size.width;
+  nscoord result;
   DISPLAY_MIN_WIDTH(this, result);
+
+  if (HasVideoElement()) {
+    nsSize size = GetVideoIntrinsicSize(aRenderingContext);
+    result = GetWritingMode().IsVertical() ? size.height : size.width;
+  } else {
+    // We expect last and only child of audio elements to be control if
+    // "controls" attribute is present.
+    nsIFrame* kid = mFrames.LastChild();
+    if (kid) {
+      result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+                                                    kid,
+                                                    nsLayoutUtils::MIN_ISIZE);
+    } else {
+      result = 0;
+    }
+  }
+
   return result;
 }
 
 nscoord nsVideoFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
 {
-  nsSize size = GetVideoIntrinsicSize(aRenderingContext);
-  nscoord result = GetWritingMode().IsVertical() ? size.height : size.width;
+  nscoord result;
   DISPLAY_PREF_WIDTH(this, result);
+
+  if (HasVideoElement()) {
+    nsSize size = GetVideoIntrinsicSize(aRenderingContext);
+    result = GetWritingMode().IsVertical() ? size.height : size.width;
+  } else {
+    // We expect last and only child of audio elements to be control if
+    // "controls" attribute is present.
+    nsIFrame* kid = mFrames.LastChild();
+    if (kid) {
+      result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
+                                                    kid,
+                                                    nsLayoutUtils::PREF_ISIZE);
+    } else {
+      result = 0;
+    }
+  }
+
   return result;
 }
 
 nsSize nsVideoFrame::GetIntrinsicRatio()
 {
   if (!HasVideoElement()) {
     // Audio elements have no intrinsic ratio.
     return nsSize(0, 0);
@@ -595,26 +672,30 @@ bool nsVideoFrame::ShouldDisplayPoster()
 }
 
 nsSize
 nsVideoFrame::GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext)
 {
   // Defaulting size to 300x150 if no size given.
   nsIntSize size(300, 150);
 
+// All media controls have been converted to HTML except Android. Hence
+// we keep this codepath for Android until removal in bug 1310907.
+#ifdef ANDROID
   if (!HasVideoElement()) {
     if (!mFrames.FirstChild()) {
       return nsSize(0, 0);
     }
 
     // Ask the controls frame what its preferred height is
     nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0);
     nscoord prefHeight = mFrames.LastChild()->GetXULPrefSize(boxState).height;
     return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight);
   }
+#endif // ANDROID
 
   HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
   if (NS_FAILED(element->GetVideoSize(&size)) && ShouldDisplayPoster()) {
     // Use the poster image frame's size.
     nsIFrame *child = mPosterImage->GetPrimaryFrame();
     nsImageFrame* imageFrame = do_QueryFrame(child);
     nsSize imgsize;
     if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) {
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -758,16 +758,18 @@ audio:not([controls]) {
 
 *|*::-moz-html-canvas-content {
   display: block !important;
   /* we want to be an absolute and fixed container */
   transform: translate(0) !important;
 }
 
 video > .caption-box {
+  width: 100%;
+  height: 100%;
   position: relative;
   overflow: hidden;
 }
 
 /* datetime elements */
 
 input[type="time"] > xul|datetimebox {
   display: flex;