forked from mirrors/gecko-dev
Bug 1431255 - Part II, Create a Shadow Root in HTMLMediaElement when enabled, skipping <xul:videocontrols> r=dholbert,smaug
This prevents XBL binding from being attached, and create the Shadow Root to host controls to be created by the script. Shadow Root and the JS controls are lazily constructed when the controls attribute is set. Set nsVideoFrame as dynamic-leaf so it will ignore content child frames when the controls are XBL anonymous content, and handles child frames from controls in the Shadow DOM. The content nodes are still ignored since there is no <slot>s in our Shadow DOM. MozReview-Commit-ID: 3hk41iMa07n --HG-- extra : rebase_source : f6f8a3facc9d83f5626cf5f3b4e3fa27438a8a8f
This commit is contained in:
parent
e1bae5c835
commit
8cc930296b
7 changed files with 120 additions and 16 deletions
|
|
@ -4510,6 +4510,17 @@ HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID,
|
|||
if (mDecoder) {
|
||||
mDecoder->SetLooping(!!aValue);
|
||||
}
|
||||
} else if (nsContentUtils::IsUAWidgetEnabled() &&
|
||||
aName == nsGkAtoms::controls &&
|
||||
IsInComposedDoc()) {
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetAttributeChanged"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
// This has to happen at this tick so that UA Widget could respond
|
||||
// before returning to content script.
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4555,6 +4566,34 @@ HTMLMediaElement::BindToTree(nsIDocument* aDocument,
|
|||
nsresult rv = nsGenericHTMLElement::BindToTree(
|
||||
aDocument, aParent, aBindingParent);
|
||||
|
||||
if (nsContentUtils::IsUAWidgetEnabled() && IsInComposedDoc()) {
|
||||
// Construct Shadow Root so web content can be hidden in the DOM.
|
||||
AttachAndSetUAShadowRoot();
|
||||
#ifdef ANDROID
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetBindToTree"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
#else
|
||||
// We don't want to call into JS if the website never asks for native
|
||||
// video controls.
|
||||
// If controls attribute is set later, controls is constructed lazily
|
||||
// with the UAWidgetAttributeChanged event.
|
||||
// This only applies to Desktop because on Fennec we would need to show
|
||||
// an UI if the video is blocked.
|
||||
if (Controls()) {
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetBindToTree"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mUnboundFromTree = false;
|
||||
|
||||
if (aDocument) {
|
||||
|
|
@ -4804,6 +4843,13 @@ HTMLMediaElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
MOZ_ASSERT(IsHidden());
|
||||
NotifyDecoderActivityChanges();
|
||||
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
|
||||
RefPtr<HTMLMediaElement> self(this);
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableFunction("dom::HTMLMediaElement::UnbindFromTree", [self]() {
|
||||
|
|
@ -4892,6 +4938,17 @@ HTMLMediaElement::AssertReadyStateIsNothing()
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::AttachAndSetUAShadowRoot()
|
||||
{
|
||||
if (GetShadowRoot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a closed shadow root to host video controls
|
||||
AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLMediaElement::InitializeDecoderAsClone(ChannelMediaDecoder* aOriginal)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1886,6 +1886,9 @@ private:
|
|||
|
||||
// For debugging bug 1407148.
|
||||
void AssertReadyStateIsNothing();
|
||||
|
||||
// Attach UA Shadow Root if it is not attached.
|
||||
void AttachAndSetUAShadowRoot();
|
||||
};
|
||||
|
||||
// Check if the context is chrome or has the debugger or tabs permission
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ TextTrackManager::UpdateCueDisplay()
|
|||
|
||||
nsCOMPtr<nsIContent> overlay = videoFrame->GetCaptionOverlay();
|
||||
nsCOMPtr<nsIContent> controls = videoFrame->GetVideoControls();
|
||||
if (!overlay) {
|
||||
if (!overlay || !controls) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1012,8 +1012,14 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
var controlBar;
|
||||
var controlBarShown;
|
||||
if (controls) {
|
||||
controlBar = controls.ownerDocument.getAnonymousElementByAttribute(
|
||||
controls, "anonid", "controlBar");
|
||||
if (controls.localName == "videocontrols") {
|
||||
// controls is a NAC; The control bar is in a XBL binding.
|
||||
controlBar = controls.ownerDocument.getAnonymousElementByAttribute(
|
||||
controls, "anonid", "controlBar");
|
||||
} else {
|
||||
// controls is a <div> that is the children of the UA Widget Shadow Root.
|
||||
controlBar = controls.parentNode.getElementById("controlBar");
|
||||
}
|
||||
controlBarShown = controlBar ? !!controlBar.clientHeight : false;
|
||||
}
|
||||
|
||||
|
|
@ -1381,7 +1387,7 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
}
|
||||
|
||||
if (self.state === "ID") {
|
||||
// If there is no cue identifier, read the next line.
|
||||
// If there is no cue identifier, read the next line.
|
||||
if (line == "") {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ FRAME_ID(nsTextFrame, Text, Leaf)
|
|||
FRAME_ID(nsTitleBarFrame, Box, NotLeaf)
|
||||
FRAME_ID(nsTreeBodyFrame, LeafBox, Leaf)
|
||||
FRAME_ID(nsTreeColFrame, Box, NotLeaf)
|
||||
FRAME_ID(nsVideoFrame, HTMLVideo, Leaf)
|
||||
FRAME_ID(nsVideoFrame, HTMLVideo, DynamicLeaf)
|
||||
FRAME_ID(nsXULLabelFrame, XULLabel, NotLeaf)
|
||||
FRAME_ID(nsXULScrollFrame, Scroll, NotLeaf)
|
||||
FRAME_ID(ViewportFrame, Viewport, NotLeaf)
|
||||
|
|
|
|||
|
|
@ -146,9 +146,11 @@ nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
|
|||
nsINode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
|
||||
if (!aElements.AppendElement(mVideoControls))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (!nsContentUtils::IsUAWidgetEnabled()) {
|
||||
NS_TrustedNewXULElement(getter_AddRefs(mVideoControls), nodeInfo.forget());
|
||||
if (!aElements.AppendElement(mVideoControls))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -170,6 +172,19 @@ nsVideoFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
|
|||
}
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsVideoFrame::GetVideoControls()
|
||||
{
|
||||
if (mVideoControls) {
|
||||
return mVideoControls;
|
||||
}
|
||||
if (mContent->GetShadowRoot()) {
|
||||
// The video controls <div> is the only child of the UA Widget Shadow Root.
|
||||
return mContent->GetShadowRoot()->GetFirstChild();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsVideoFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
|
||||
{
|
||||
|
|
@ -306,6 +321,8 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
|
|||
|
||||
nsMargin borderPadding = aReflowInput.ComputedPhysicalBorderPadding();
|
||||
|
||||
nsIContent* videoControlsDiv = GetVideoControls();
|
||||
|
||||
// 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.
|
||||
|
|
@ -349,7 +366,7 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
|
|||
posterRenderRect.x, posterRenderRect.y, 0);
|
||||
|
||||
} else if (child->GetContent() == mCaptionDiv ||
|
||||
child->GetContent() == mVideoControls) {
|
||||
child->GetContent() == videoControlsDiv) {
|
||||
// Reflow the caption and control bar frames.
|
||||
WritingMode wm = child->GetWritingMode();
|
||||
LogicalSize availableSize = aReflowInput.ComputedSize(wm);
|
||||
|
|
@ -366,7 +383,7 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
|
|||
"We gave our child unconstrained available block-size, "
|
||||
"so it should be complete!");
|
||||
|
||||
if (child->GetContent() == mVideoControls && isBSizeShrinkWrapping) {
|
||||
if (child->GetContent() == videoControlsDiv && isBSizeShrinkWrapping) {
|
||||
// Resolve our own BSize based on the controls' size in the same axis.
|
||||
contentBoxBSize = myWM.IsOrthogonalTo(wm) ?
|
||||
kidDesiredSize.ISize(wm) : kidDesiredSize.BSize(wm);
|
||||
|
|
@ -375,11 +392,14 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
|
|||
FinishReflowChild(child, aPresContext,
|
||||
kidDesiredSize, &kidReflowInput,
|
||||
borderPadding.left, borderPadding.top, 0);
|
||||
}
|
||||
|
||||
if (child->GetContent() == mVideoControls && child->GetSize() != oldChildSize) {
|
||||
RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent());
|
||||
nsContentUtils::AddScriptRunner(event);
|
||||
if (child->GetContent() == videoControlsDiv && child->GetSize() != oldChildSize) {
|
||||
RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent());
|
||||
nsContentUtils::AddScriptRunner(event);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Extra child frame found in nsVideoFrame. "
|
||||
"Possibly from stray whitespace around the videocontrols container element.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -411,6 +431,23 @@ nsVideoFrame::Reflow(nsPresContext* aPresContext,
|
|||
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* nsVideoFrame should be a non-leaf frame when UA Widget is enabled,
|
||||
* so the videocontrols container element inserted under the Shadow Root can be
|
||||
* picked up. No frames will be generated from elements from the web content,
|
||||
* given that they have been replaced by the Shadow Root without and <slots>
|
||||
* element in the DOM tree.
|
||||
*
|
||||
* When the UA Widget is disabled, i.e. the videocontrols is bound as anonymous
|
||||
* content with XBL, nsVideoFrame has to be a leaf so no frames from web content
|
||||
* element will be generated.
|
||||
*/
|
||||
bool
|
||||
nsVideoFrame::IsLeafDynamic() const
|
||||
{
|
||||
return !nsContentUtils::IsUAWidgetEnabled();
|
||||
}
|
||||
|
||||
class nsDisplayVideo : public nsDisplayItem {
|
||||
public:
|
||||
nsDisplayVideo(nsDisplayListBuilder* aBuilder, nsVideoFrame* aFrame)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ public:
|
|||
const ReflowInput& aReflowInput,
|
||||
nsReflowStatus& aStatus) override;
|
||||
|
||||
bool IsLeafDynamic() const override;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
mozilla::a11y::AccType AccessibleType() override;
|
||||
#endif
|
||||
|
|
@ -95,8 +97,7 @@ public:
|
|||
bool ShouldDisplayPoster();
|
||||
|
||||
nsIContent *GetCaptionOverlay() { return mCaptionDiv; }
|
||||
|
||||
nsIContent *GetVideoControls() { return mVideoControls; }
|
||||
nsIContent *GetVideoControls();
|
||||
|
||||
#ifdef DEBUG_FRAME_DUMP
|
||||
nsresult GetFrameName(nsAString& aResult) const override;
|
||||
|
|
|
|||
Loading…
Reference in a new issue