layout/generic/nsVideoFrame.cpp

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

mercurial