|
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 } |