layout/generic/nsVideoFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* rendering object for the HTML <video> element */
michael@0 8
michael@0 9 #include "nsVideoFrame.h"
michael@0 10
michael@0 11 #include "nsCOMPtr.h"
michael@0 12 #include "nsGkAtoms.h"
michael@0 13
michael@0 14 #include "mozilla/dom/HTMLVideoElement.h"
michael@0 15 #include "nsIDOMHTMLVideoElement.h"
michael@0 16 #include "nsIDOMHTMLImageElement.h"
michael@0 17 #include "nsDisplayList.h"
michael@0 18 #include "nsGenericHTMLElement.h"
michael@0 19 #include "nsPresContext.h"
michael@0 20 #include "nsContentCreatorFunctions.h"
michael@0 21 #include "nsBoxLayoutState.h"
michael@0 22 #include "nsBoxFrame.h"
michael@0 23 #include "nsImageFrame.h"
michael@0 24 #include "nsIImageLoadingContent.h"
michael@0 25 #include "nsContentUtils.h"
michael@0 26 #include "ImageContainer.h"
michael@0 27 #include "ImageLayers.h"
michael@0 28 #include "nsContentList.h"
michael@0 29 #include <algorithm>
michael@0 30
michael@0 31 using namespace mozilla;
michael@0 32 using namespace mozilla::layers;
michael@0 33 using namespace mozilla::dom;
michael@0 34 using namespace mozilla::gfx;
michael@0 35
michael@0 36 nsIFrame*
michael@0 37 NS_NewHTMLVideoFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 38 {
michael@0 39 return new (aPresShell) nsVideoFrame(aContext);
michael@0 40 }
michael@0 41
michael@0 42 NS_IMPL_FRAMEARENA_HELPERS(nsVideoFrame)
michael@0 43
michael@0 44 nsVideoFrame::nsVideoFrame(nsStyleContext* aContext) :
michael@0 45 nsContainerFrame(aContext)
michael@0 46 {
michael@0 47 }
michael@0 48
michael@0 49 nsVideoFrame::~nsVideoFrame()
michael@0 50 {
michael@0 51 }
michael@0 52
michael@0 53 NS_QUERYFRAME_HEAD(nsVideoFrame)
michael@0 54 NS_QUERYFRAME_ENTRY(nsVideoFrame)
michael@0 55 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
michael@0 56 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
michael@0 57
michael@0 58 nsresult
michael@0 59 nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
michael@0 60 {
michael@0 61 nsNodeInfoManager *nodeInfoManager = GetContent()->GetCurrentDoc()->NodeInfoManager();
michael@0 62 nsCOMPtr<nsINodeInfo> nodeInfo;
michael@0 63 Element *element;
michael@0 64
michael@0 65 if (HasVideoElement()) {
michael@0 66 // Create an anonymous image element as a child to hold the poster
michael@0 67 // image. We may not have a poster image now, but one could be added
michael@0 68 // before we load, or on a subsequent load.
michael@0 69 nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::img,
michael@0 70 nullptr,
michael@0 71 kNameSpaceID_XHTML,
michael@0 72 nsIDOMNode::ELEMENT_NODE);
michael@0 73 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
michael@0 74 element = NS_NewHTMLImageElement(nodeInfo.forget());
michael@0 75 mPosterImage = element;
michael@0 76 NS_ENSURE_TRUE(mPosterImage, NS_ERROR_OUT_OF_MEMORY);
michael@0 77
michael@0 78 // Set the nsImageLoadingContent::ImageState() to 0. This means that the
michael@0 79 // image will always report its state as 0, so it will never be reframed
michael@0 80 // to show frames for loading or the broken image icon. This is important,
michael@0 81 // as the image is native anonymous, and so can't be reframed (currently).
michael@0 82 nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
michael@0 83 NS_ENSURE_TRUE(imgContent, NS_ERROR_FAILURE);
michael@0 84
michael@0 85 imgContent->ForceImageState(true, 0);
michael@0 86 // And now have it update its internal state
michael@0 87 element->UpdateState(false);
michael@0 88
michael@0 89 UpdatePosterSource(false);
michael@0 90
michael@0 91 if (!aElements.AppendElement(mPosterImage))
michael@0 92 return NS_ERROR_OUT_OF_MEMORY;
michael@0 93
michael@0 94 // Set up the caption overlay div for showing any TextTrack data
michael@0 95 nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::div,
michael@0 96 nullptr,
michael@0 97 kNameSpaceID_XHTML,
michael@0 98 nsIDOMNode::ELEMENT_NODE);
michael@0 99 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
michael@0 100 mCaptionDiv = NS_NewHTMLDivElement(nodeInfo.forget());
michael@0 101 NS_ENSURE_TRUE(mCaptionDiv, NS_ERROR_OUT_OF_MEMORY);
michael@0 102 nsGenericHTMLElement* div = static_cast<nsGenericHTMLElement*>(mCaptionDiv.get());
michael@0 103 div->SetClassName(NS_LITERAL_STRING("caption-box"));
michael@0 104
michael@0 105 if (!aElements.AppendElement(mCaptionDiv))
michael@0 106 return NS_ERROR_OUT_OF_MEMORY;
michael@0 107 }
michael@0 108
michael@0 109 // Set up "videocontrols" XUL element which will be XBL-bound to the
michael@0 110 // actual controls.
michael@0 111 nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::videocontrols,
michael@0 112 nullptr,
michael@0 113 kNameSpaceID_XUL,
michael@0 114 nsIDOMNode::ELEMENT_NODE);
michael@0 115 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
michael@0 116
michael@0 117 NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
michael@0 118 if (!aElements.AppendElement(mVideoControls))
michael@0 119 return NS_ERROR_OUT_OF_MEMORY;
michael@0 120
michael@0 121 return NS_OK;
michael@0 122 }
michael@0 123
michael@0 124 void
michael@0 125 nsVideoFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
michael@0 126 uint32_t aFliter)
michael@0 127 {
michael@0 128 aElements.MaybeAppendElement(mPosterImage);
michael@0 129 aElements.MaybeAppendElement(mVideoControls);
michael@0 130 aElements.MaybeAppendElement(mCaptionDiv);
michael@0 131 }
michael@0 132
michael@0 133 void
michael@0 134 nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 135 {
michael@0 136 nsContentUtils::DestroyAnonymousContent(&mCaptionDiv);
michael@0 137 nsContentUtils::DestroyAnonymousContent(&mVideoControls);
michael@0 138 nsContentUtils::DestroyAnonymousContent(&mPosterImage);
michael@0 139 nsContainerFrame::DestroyFrom(aDestructRoot);
michael@0 140 }
michael@0 141
michael@0 142 bool
michael@0 143 nsVideoFrame::IsLeaf() const
michael@0 144 {
michael@0 145 return true;
michael@0 146 }
michael@0 147
michael@0 148 // Return the largest rectangle that fits in aRect and has the
michael@0 149 // same aspect ratio as aRatio, centered at the center of aRect
michael@0 150 static gfxRect
michael@0 151 CorrectForAspectRatio(const gfxRect& aRect, const nsIntSize& aRatio)
michael@0 152 {
michael@0 153 NS_ASSERTION(aRatio.width > 0 && aRatio.height > 0 && !aRect.IsEmpty(),
michael@0 154 "Nothing to draw");
michael@0 155 // Choose scale factor that scales aRatio to just fit into aRect
michael@0 156 gfxFloat scale =
michael@0 157 std::min(aRect.Width()/aRatio.width, aRect.Height()/aRatio.height);
michael@0 158 gfxSize scaledRatio(scale*aRatio.width, scale*aRatio.height);
michael@0 159 gfxPoint topLeft((aRect.Width() - scaledRatio.width)/2,
michael@0 160 (aRect.Height() - scaledRatio.height)/2);
michael@0 161 return gfxRect(aRect.TopLeft() + topLeft, scaledRatio);
michael@0 162 }
michael@0 163
michael@0 164 already_AddRefed<Layer>
michael@0 165 nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
michael@0 166 LayerManager* aManager,
michael@0 167 nsDisplayItem* aItem,
michael@0 168 const ContainerLayerParameters& aContainerParameters)
michael@0 169 {
michael@0 170 nsRect area = GetContentRect() - GetPosition() + aItem->ToReferenceFrame();
michael@0 171 HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
michael@0 172 nsIntSize videoSize;
michael@0 173 if (NS_FAILED(element->GetVideoSize(&videoSize)) || area.IsEmpty()) {
michael@0 174 return nullptr;
michael@0 175 }
michael@0 176
michael@0 177 nsRefPtr<ImageContainer> container = element->GetImageContainer();
michael@0 178 if (!container)
michael@0 179 return nullptr;
michael@0 180
michael@0 181 // Retrieve the size of the decoded video frame, before being scaled
michael@0 182 // by pixel aspect ratio.
michael@0 183 mozilla::gfx::IntSize frameSize = container->GetCurrentSize();
michael@0 184 if (frameSize.width == 0 || frameSize.height == 0) {
michael@0 185 // No image, or zero-sized image. No point creating a layer.
michael@0 186 return nullptr;
michael@0 187 }
michael@0 188
michael@0 189 // Compute the rectangle in which to paint the video. We need to use
michael@0 190 // the largest rectangle that fills our content-box and has the
michael@0 191 // correct aspect ratio.
michael@0 192 nsPresContext* presContext = PresContext();
michael@0 193 gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
michael@0 194 presContext->AppUnitsToGfxUnits(area.y),
michael@0 195 presContext->AppUnitsToGfxUnits(area.width),
michael@0 196 presContext->AppUnitsToGfxUnits(area.height));
michael@0 197 r = CorrectForAspectRatio(r, videoSize);
michael@0 198 r.Round();
michael@0 199 if (r.IsEmpty()) {
michael@0 200 return nullptr;
michael@0 201 }
michael@0 202 IntSize scaleHint(static_cast<int32_t>(r.Width()),
michael@0 203 static_cast<int32_t>(r.Height()));
michael@0 204 container->SetScaleHint(scaleHint);
michael@0 205
michael@0 206 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
michael@0 207 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
michael@0 208 if (!layer) {
michael@0 209 layer = aManager->CreateImageLayer();
michael@0 210 if (!layer)
michael@0 211 return nullptr;
michael@0 212 }
michael@0 213
michael@0 214 layer->SetContainer(container);
michael@0 215 layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
michael@0 216 layer->SetContentFlags(Layer::CONTENT_OPAQUE);
michael@0 217 // Set a transform on the layer to draw the video in the right place
michael@0 218 gfx::Matrix transform;
michael@0 219 gfxPoint p = r.TopLeft() + aContainerParameters.mOffset;
michael@0 220 transform.Translate(p.x, p.y);
michael@0 221 transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height);
michael@0 222 layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
michael@0 223 layer->SetVisibleRegion(nsIntRect(0, 0, frameSize.width, frameSize.height));
michael@0 224 nsRefPtr<Layer> result = layer.forget();
michael@0 225 return result.forget();
michael@0 226 }
michael@0 227
michael@0 228 class DispatchResizeToControls : public nsRunnable
michael@0 229 {
michael@0 230 public:
michael@0 231 DispatchResizeToControls(nsIContent* aContent)
michael@0 232 : mContent(aContent) {}
michael@0 233 NS_IMETHOD Run() MOZ_OVERRIDE {
michael@0 234 nsContentUtils::DispatchTrustedEvent(mContent->OwnerDoc(), mContent,
michael@0 235 NS_LITERAL_STRING("resizevideocontrols"),
michael@0 236 false, false);
michael@0 237 return NS_OK;
michael@0 238 }
michael@0 239 nsCOMPtr<nsIContent> mContent;
michael@0 240 };
michael@0 241
michael@0 242 nsresult
michael@0 243 nsVideoFrame::Reflow(nsPresContext* aPresContext,
michael@0 244 nsHTMLReflowMetrics& aMetrics,
michael@0 245 const nsHTMLReflowState& aReflowState,
michael@0 246 nsReflowStatus& aStatus)
michael@0 247 {
michael@0 248 DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
michael@0 249 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
michael@0 250 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
michael@0 251 ("enter nsVideoFrame::Reflow: availSize=%d,%d",
michael@0 252 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
michael@0 253
michael@0 254 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
michael@0 255
michael@0 256 aStatus = NS_FRAME_COMPLETE;
michael@0 257
michael@0 258 aMetrics.Width() = aReflowState.ComputedWidth();
michael@0 259 aMetrics.Height() = aReflowState.ComputedHeight();
michael@0 260
michael@0 261 // stash this away so we can compute our inner area later
michael@0 262 mBorderPadding = aReflowState.ComputedPhysicalBorderPadding();
michael@0 263
michael@0 264 aMetrics.Width() += mBorderPadding.left + mBorderPadding.right;
michael@0 265 aMetrics.Height() += mBorderPadding.top + mBorderPadding.bottom;
michael@0 266
michael@0 267 // Reflow the child frames. We may have up to two, an image frame
michael@0 268 // which is the poster, and a box frame, which is the video controls.
michael@0 269 for (nsIFrame *child = mFrames.FirstChild();
michael@0 270 child;
michael@0 271 child = child->GetNextSibling()) {
michael@0 272 if (child->GetContent() == mPosterImage) {
michael@0 273 // Reflow the poster frame.
michael@0 274 nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child);
michael@0 275 nsHTMLReflowMetrics kidDesiredSize(aReflowState);
michael@0 276 nsSize availableSize = nsSize(aReflowState.AvailableWidth(),
michael@0 277 aReflowState.AvailableHeight());
michael@0 278 nsHTMLReflowState kidReflowState(aPresContext,
michael@0 279 aReflowState,
michael@0 280 imageFrame,
michael@0 281 availableSize,
michael@0 282 aMetrics.Width(),
michael@0 283 aMetrics.Height());
michael@0 284
michael@0 285 uint32_t posterHeight, posterWidth;
michael@0 286 nsSize scaledPosterSize(0, 0);
michael@0 287 nsSize computedArea(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
michael@0 288 nsPoint posterTopLeft(0, 0);
michael@0 289
michael@0 290 nsCOMPtr<nsIDOMHTMLImageElement> posterImage = do_QueryInterface(mPosterImage);
michael@0 291 NS_ENSURE_TRUE(posterImage, NS_ERROR_FAILURE);
michael@0 292 posterImage->GetNaturalHeight(&posterHeight);
michael@0 293 posterImage->GetNaturalWidth(&posterWidth);
michael@0 294
michael@0 295 if (ShouldDisplayPoster() && posterHeight && posterWidth) {
michael@0 296 gfxFloat scale =
michael@0 297 std::min(static_cast<float>(computedArea.width)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterWidth)),
michael@0 298 static_cast<float>(computedArea.height)/nsPresContext::CSSPixelsToAppUnits(static_cast<float>(posterHeight)));
michael@0 299 gfxSize scaledRatio = gfxSize(scale*posterWidth, scale*posterHeight);
michael@0 300 scaledPosterSize.width = nsPresContext::CSSPixelsToAppUnits(static_cast<float>(scaledRatio.width));
michael@0 301 scaledPosterSize.height = nsPresContext::CSSPixelsToAppUnits(static_cast<int32_t>(scaledRatio.height));
michael@0 302 }
michael@0 303 kidReflowState.SetComputedWidth(scaledPosterSize.width);
michael@0 304 kidReflowState.SetComputedHeight(scaledPosterSize.height);
michael@0 305 posterTopLeft.x = ((computedArea.width - scaledPosterSize.width) / 2) + mBorderPadding.left;
michael@0 306 posterTopLeft.y = ((computedArea.height - scaledPosterSize.height) / 2) + mBorderPadding.top;
michael@0 307
michael@0 308 ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState,
michael@0 309 posterTopLeft.x, posterTopLeft.y, 0, aStatus);
michael@0 310 FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowState,
michael@0 311 posterTopLeft.x, posterTopLeft.y, 0);
michael@0 312 } else if (child->GetContent() == mVideoControls) {
michael@0 313 // Reflow the video controls frame.
michael@0 314 nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext);
michael@0 315 nsSize size = child->GetSize();
michael@0 316 nsBoxFrame::LayoutChildAt(boxState,
michael@0 317 child,
michael@0 318 nsRect(mBorderPadding.left,
michael@0 319 mBorderPadding.top,
michael@0 320 aReflowState.ComputedWidth(),
michael@0 321 aReflowState.ComputedHeight()));
michael@0 322 if (child->GetSize() != size) {
michael@0 323 nsRefPtr<nsRunnable> event = new DispatchResizeToControls(child->GetContent());
michael@0 324 nsContentUtils::AddScriptRunner(event);
michael@0 325 }
michael@0 326 } else if (child->GetContent() == mCaptionDiv) {
michael@0 327 // Reflow to caption div
michael@0 328 nsHTMLReflowMetrics kidDesiredSize(aReflowState);
michael@0 329 nsSize availableSize = nsSize(aReflowState.AvailableWidth(),
michael@0 330 aReflowState.AvailableHeight());
michael@0 331 nsHTMLReflowState kidReflowState(aPresContext,
michael@0 332 aReflowState,
michael@0 333 child,
michael@0 334 availableSize,
michael@0 335 aMetrics.Width(),
michael@0 336 aMetrics.Height());
michael@0 337 nsSize size(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
michael@0 338 size.width -= kidReflowState.ComputedPhysicalBorderPadding().LeftRight();
michael@0 339 size.height -= kidReflowState.ComputedPhysicalBorderPadding().TopBottom();
michael@0 340
michael@0 341 kidReflowState.SetComputedWidth(std::max(size.width, 0));
michael@0 342 kidReflowState.SetComputedHeight(std::max(size.height, 0));
michael@0 343
michael@0 344 ReflowChild(child, aPresContext, kidDesiredSize, kidReflowState,
michael@0 345 mBorderPadding.left, mBorderPadding.top, 0, aStatus);
michael@0 346 FinishReflowChild(child, aPresContext,
michael@0 347 kidDesiredSize, &kidReflowState,
michael@0 348 mBorderPadding.left, mBorderPadding.top, 0);
michael@0 349 }
michael@0 350 }
michael@0 351 aMetrics.SetOverflowAreasToDesiredBounds();
michael@0 352
michael@0 353 FinishAndStoreOverflow(&aMetrics);
michael@0 354
michael@0 355 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
michael@0 356 ("exit nsVideoFrame::Reflow: size=%d,%d",
michael@0 357 aMetrics.Width(), aMetrics.Height()));
michael@0 358 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
michael@0 359
michael@0 360 return NS_OK;
michael@0 361 }
michael@0 362
michael@0 363 class nsDisplayVideo : public nsDisplayItem {
michael@0 364 public:
michael@0 365 nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame)
michael@0 366 : nsDisplayItem(aBuilder, aFrame)
michael@0 367 {
michael@0 368 MOZ_COUNT_CTOR(nsDisplayVideo);
michael@0 369 }
michael@0 370 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 371 virtual ~nsDisplayVideo() {
michael@0 372 MOZ_COUNT_DTOR(nsDisplayVideo);
michael@0 373 }
michael@0 374 #endif
michael@0 375
michael@0 376 NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO)
michael@0 377
michael@0 378 // It would be great if we could override GetOpaqueRegion to return nonempty here,
michael@0 379 // but it's probably not safe to do so in general. Video frames are
michael@0 380 // updated asynchronously from decoder threads, and it's possible that
michael@0 381 // we might have an opaque video frame when GetOpaqueRegion is called, but
michael@0 382 // when we come to paint, the video frame is transparent or has gone
michael@0 383 // away completely (e.g. because of a decoder error). The problem would
michael@0 384 // be especially acute if we have off-main-thread rendering.
michael@0 385
michael@0 386 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
michael@0 387 {
michael@0 388 *aSnap = true;
michael@0 389 nsIFrame* f = Frame();
michael@0 390 return f->GetContentRect() - f->GetPosition() + ToReferenceFrame();
michael@0 391 }
michael@0 392
michael@0 393 virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
michael@0 394 LayerManager* aManager,
michael@0 395 const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE
michael@0 396 {
michael@0 397 return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
michael@0 398 }
michael@0 399
michael@0 400 virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
michael@0 401 LayerManager* aManager,
michael@0 402 const ContainerLayerParameters& aParameters) MOZ_OVERRIDE
michael@0 403 {
michael@0 404 if (aManager->IsCompositingCheap()) {
michael@0 405 // Since ImageLayers don't require additional memory of the
michael@0 406 // video frames we have to have anyway, we can't save much by
michael@0 407 // making layers inactive. Also, for many accelerated layer
michael@0 408 // managers calling imageContainer->GetCurrentAsSurface can be
michael@0 409 // very expensive. So just always be active when compositing is
michael@0 410 // cheap (i.e. hardware accelerated).
michael@0 411 return LAYER_ACTIVE;
michael@0 412 }
michael@0 413 HTMLMediaElement* elem =
michael@0 414 static_cast<HTMLMediaElement*>(mFrame->GetContent());
michael@0 415 return elem->IsPotentiallyPlaying() ? LAYER_ACTIVE_FORCE : LAYER_INACTIVE;
michael@0 416 }
michael@0 417 };
michael@0 418
michael@0 419 void
michael@0 420 nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 421 const nsRect& aDirtyRect,
michael@0 422 const nsDisplayListSet& aLists)
michael@0 423 {
michael@0 424 if (!IsVisibleForPainting(aBuilder))
michael@0 425 return;
michael@0 426
michael@0 427 DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame");
michael@0 428
michael@0 429 DisplayBorderBackgroundOutline(aBuilder, aLists);
michael@0 430
michael@0 431 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
michael@0 432 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
michael@0 433
michael@0 434 if (HasVideoElement() && !ShouldDisplayPoster()) {
michael@0 435 aLists.Content()->AppendNewToTop(
michael@0 436 new (aBuilder) nsDisplayVideo(aBuilder, this));
michael@0 437 }
michael@0 438
michael@0 439 // Add child frames to display list. We expect various children,
michael@0 440 // but only want to draw mPosterImage conditionally. Others we
michael@0 441 // always add to the display list.
michael@0 442 for (nsIFrame *child = mFrames.FirstChild();
michael@0 443 child;
michael@0 444 child = child->GetNextSibling()) {
michael@0 445 if (child->GetContent() != mPosterImage || ShouldDisplayPoster()) {
michael@0 446 child->BuildDisplayListForStackingContext(aBuilder,
michael@0 447 aDirtyRect - child->GetOffsetTo(this),
michael@0 448 aLists.Content());
michael@0 449 } else if (child->GetType() == nsGkAtoms::boxFrame) {
michael@0 450 child->BuildDisplayListForStackingContext(aBuilder,
michael@0 451 aDirtyRect - child->GetOffsetTo(this),
michael@0 452 aLists.Content());
michael@0 453 }
michael@0 454 }
michael@0 455 }
michael@0 456
michael@0 457 nsIAtom*
michael@0 458 nsVideoFrame::GetType() const
michael@0 459 {
michael@0 460 return nsGkAtoms::HTMLVideoFrame;
michael@0 461 }
michael@0 462
michael@0 463 #ifdef ACCESSIBILITY
michael@0 464 a11y::AccType
michael@0 465 nsVideoFrame::AccessibleType()
michael@0 466 {
michael@0 467 return a11y::eHTMLMediaType;
michael@0 468 }
michael@0 469 #endif
michael@0 470
michael@0 471 #ifdef DEBUG_FRAME_DUMP
michael@0 472 nsresult
michael@0 473 nsVideoFrame::GetFrameName(nsAString& aResult) const
michael@0 474 {
michael@0 475 return MakeFrameName(NS_LITERAL_STRING("HTMLVideo"), aResult);
michael@0 476 }
michael@0 477 #endif
michael@0 478
michael@0 479 nsSize nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext,
michael@0 480 nsSize aCBSize,
michael@0 481 nscoord aAvailableWidth,
michael@0 482 nsSize aMargin,
michael@0 483 nsSize aBorder,
michael@0 484 nsSize aPadding,
michael@0 485 uint32_t aFlags)
michael@0 486 {
michael@0 487 nsSize size = GetVideoIntrinsicSize(aRenderingContext);
michael@0 488
michael@0 489 IntrinsicSize intrinsicSize;
michael@0 490 intrinsicSize.width.SetCoordValue(size.width);
michael@0 491 intrinsicSize.height.SetCoordValue(size.height);
michael@0 492
michael@0 493 // Only video elements have an intrinsic ratio.
michael@0 494 nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0);
michael@0 495
michael@0 496 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aRenderingContext,
michael@0 497 this,
michael@0 498 intrinsicSize,
michael@0 499 intrinsicRatio,
michael@0 500 aCBSize,
michael@0 501 aMargin,
michael@0 502 aBorder,
michael@0 503 aPadding);
michael@0 504 }
michael@0 505
michael@0 506 nscoord nsVideoFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 507 {
michael@0 508 nscoord result = GetVideoIntrinsicSize(aRenderingContext).width;
michael@0 509 DISPLAY_MIN_WIDTH(this, result);
michael@0 510 return result;
michael@0 511 }
michael@0 512
michael@0 513 nscoord nsVideoFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 514 {
michael@0 515 nscoord result = GetVideoIntrinsicSize(aRenderingContext).width;
michael@0 516 DISPLAY_PREF_WIDTH(this, result);
michael@0 517 return result;
michael@0 518 }
michael@0 519
michael@0 520 nsSize nsVideoFrame::GetIntrinsicRatio()
michael@0 521 {
michael@0 522 if (!HasVideoElement()) {
michael@0 523 // Audio elements have no intrinsic ratio.
michael@0 524 return nsSize(0, 0);
michael@0 525 }
michael@0 526
michael@0 527 return GetVideoIntrinsicSize(nullptr);
michael@0 528 }
michael@0 529
michael@0 530 bool nsVideoFrame::ShouldDisplayPoster()
michael@0 531 {
michael@0 532 if (!HasVideoElement())
michael@0 533 return false;
michael@0 534
michael@0 535 HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
michael@0 536 if (element->GetPlayedOrSeeked() && HasVideoData())
michael@0 537 return false;
michael@0 538
michael@0 539 nsCOMPtr<nsIImageLoadingContent> imgContent = do_QueryInterface(mPosterImage);
michael@0 540 NS_ENSURE_TRUE(imgContent, false);
michael@0 541
michael@0 542 nsCOMPtr<imgIRequest> request;
michael@0 543 nsresult res = imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 544 getter_AddRefs(request));
michael@0 545 if (NS_FAILED(res) || !request) {
michael@0 546 return false;
michael@0 547 }
michael@0 548
michael@0 549 uint32_t status = 0;
michael@0 550 res = request->GetImageStatus(&status);
michael@0 551 if (NS_FAILED(res) || (status & imgIRequest::STATUS_ERROR))
michael@0 552 return false;
michael@0 553
michael@0 554 return true;
michael@0 555 }
michael@0 556
michael@0 557 nsSize
michael@0 558 nsVideoFrame::GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext)
michael@0 559 {
michael@0 560 // Defaulting size to 300x150 if no size given.
michael@0 561 nsIntSize size(300, 150);
michael@0 562
michael@0 563 if (!HasVideoElement()) {
michael@0 564 if (!mFrames.FirstChild()) {
michael@0 565 return nsSize(0, 0);
michael@0 566 }
michael@0 567
michael@0 568 // Ask the controls frame what its preferred height is
michael@0 569 nsBoxLayoutState boxState(PresContext(), aRenderingContext, 0);
michael@0 570 nscoord prefHeight = mFrames.LastChild()->GetPrefSize(boxState).height;
michael@0 571 return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width), prefHeight);
michael@0 572 }
michael@0 573
michael@0 574 HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
michael@0 575 if (NS_FAILED(element->GetVideoSize(&size)) && ShouldDisplayPoster()) {
michael@0 576 // Use the poster image frame's size.
michael@0 577 nsIFrame *child = mPosterImage->GetPrimaryFrame();
michael@0 578 nsImageFrame* imageFrame = do_QueryFrame(child);
michael@0 579 nsSize imgsize;
michael@0 580 if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(imgsize))) {
michael@0 581 return imgsize;
michael@0 582 }
michael@0 583 }
michael@0 584
michael@0 585 return nsSize(nsPresContext::CSSPixelsToAppUnits(size.width),
michael@0 586 nsPresContext::CSSPixelsToAppUnits(size.height));
michael@0 587 }
michael@0 588
michael@0 589 void
michael@0 590 nsVideoFrame::UpdatePosterSource(bool aNotify)
michael@0 591 {
michael@0 592 NS_ASSERTION(HasVideoElement(), "Only call this on <video> elements.");
michael@0 593 HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
michael@0 594
michael@0 595 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::poster)) {
michael@0 596 nsAutoString posterStr;
michael@0 597 element->GetPoster(posterStr);
michael@0 598 mPosterImage->SetAttr(kNameSpaceID_None,
michael@0 599 nsGkAtoms::src,
michael@0 600 posterStr,
michael@0 601 aNotify);
michael@0 602 } else {
michael@0 603 mPosterImage->UnsetAttr(kNameSpaceID_None, nsGkAtoms::poster, aNotify);
michael@0 604 }
michael@0 605 }
michael@0 606
michael@0 607 nsresult
michael@0 608 nsVideoFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 609 nsIAtom* aAttribute,
michael@0 610 int32_t aModType)
michael@0 611 {
michael@0 612 if (aAttribute == nsGkAtoms::poster && HasVideoElement()) {
michael@0 613 UpdatePosterSource(true);
michael@0 614 }
michael@0 615 return nsContainerFrame::AttributeChanged(aNameSpaceID,
michael@0 616 aAttribute,
michael@0 617 aModType);
michael@0 618 }
michael@0 619
michael@0 620 bool nsVideoFrame::HasVideoElement() {
michael@0 621 nsCOMPtr<nsIDOMHTMLVideoElement> videoDomElement = do_QueryInterface(mContent);
michael@0 622 return videoDomElement != nullptr;
michael@0 623 }
michael@0 624
michael@0 625 bool nsVideoFrame::HasVideoData()
michael@0 626 {
michael@0 627 if (!HasVideoElement())
michael@0 628 return false;
michael@0 629 HTMLVideoElement* element = static_cast<HTMLVideoElement*>(GetContent());
michael@0 630 nsIntSize size(0, 0);
michael@0 631 element->GetVideoSize(&size);
michael@0 632 return size != nsIntSize(0,0);
michael@0 633 }

mercurial