Bug 1466897 - Support context menus in top level chrome privileged HTML pages. r=mats
Create an anonymous popupgroup element during the creation of a top level
chrome privileged nsCanvasFrame. Implement nsIRootBox for nsCanvasFrame to store
the popup set frame. Adjust nsIRootBox::GetRootBox to be able to find the
popupset frame in a non-xul frame layout.
MozReview-Commit-ID: HCbPgQb4uil
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -9,16 +9,17 @@
#include "nsCanvasFrame.h"
#include "AccessibleCaretEventHub.h"
#include "gfxContext.h"
#include "gfxUtils.h"
#include "nsContainerFrame.h"
#include "nsCSSRendering.h"
#include "nsPresContext.h"
+#include "nsPopupSetFrame.h"
#include "nsGkAtoms.h"
#include "nsIFrameInlines.h"
#include "nsIPresShell.h"
#include "nsDisplayList.h"
#include "nsCSSFrameConstructor.h"
#include "nsFrameManager.h"
#include "gfxPlatform.h"
#include "nsPrintfCString.h"
@@ -47,16 +48,17 @@ NS_NewCanvasFrame(nsIPresShell* aPresShe
return new (aPresShell) nsCanvasFrame(aStyle);
}
NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
NS_QUERYFRAME_HEAD(nsCanvasFrame)
NS_QUERYFRAME_ENTRY(nsCanvasFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
+ NS_QUERYFRAME_ENTRY(nsIRootBox)
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
void
nsCanvasFrame::ShowCustomContentContainer()
{
if (mCustomContentContainer) {
mCustomContentContainer->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
}
@@ -122,25 +124,45 @@ nsCanvasFrame::CreateAnonymousContent(ns
RefPtr<AccessibleCaretEventHub> eventHub =
PresContext()->GetPresShell()->GetAccessibleCaretEventHub();
if (eventHub) {
// AccessibleCaret will insert anonymous caret elements.
eventHub->Init();
}
+ // Create a popupgroup element for chrome privileged top level non-XUL
+ // documents to support context menus.
+ if (PresContext()->IsChrome() && PresContext()->IsRoot() &&
+ doc->AllowXULXBL() && !doc->IsXULDocument()) {
+ nsNodeInfoManager* nodeInfoManager = doc->NodeInfoManager();
+ RefPtr<NodeInfo> nodeInfo =
+ nodeInfoManager->GetNodeInfo(nsGkAtoms::popupgroup,
+ nullptr, kNameSpaceID_XUL,
+ nsINode::ELEMENT_NODE);
+
+ rv = NS_NewXULElement(getter_AddRefs(mPopupgroupContent),
+ nodeInfo.forget(), dom::NOT_FROM_PARSER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aElements.AppendElement(mPopupgroupContent);
+ }
+
return NS_OK;
}
void
nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter)
{
if (mCustomContentContainer) {
aElements.AppendElement(mCustomContentContainer);
}
+ if (mPopupgroupContent) {
+ aElements.AppendElement(mPopupgroupContent);
+ }
}
void
nsCanvasFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
{
nsIScrollableFrame* sf =
PresContext()->GetPresShell()->GetRootScrollFrameAsScrollable();
if (sf) {
@@ -158,17 +180,23 @@ nsCanvasFrame::DestroyFrom(nsIFrame* aDe
doc->GetAnonymousContents();
for (size_t i = 0, len = docAnonContents.Length(); i < len; ++i) {
AnonymousContent* content = docAnonContents[i];
nsCOMPtr<nsINode> clonedElement = content->GetContentNode()->CloneNode(true, rv);
content->SetContentNode(clonedElement->AsElement());
}
}
aPostDestroyData.AddAnonymousContent(mCustomContentContainer.forget());
+ if (mPopupgroupContent) {
+ aPostDestroyData.AddAnonymousContent(mPopupgroupContent.forget());
+ }
+ MOZ_ASSERT(!mPopupSetFrame ||
+ nsLayoutUtils::IsProperAncestorFrame(this, mPopupSetFrame),
+ "Someone forgot to clear popup set frame");
nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
}
void
nsCanvasFrame::ScrollPositionWillChange(nscoord aX, nscoord aY)
{
if (mDoPaintFocus) {
mDoPaintFocus = false;
@@ -257,16 +285,44 @@ nsRect nsCanvasFrame::CanvasArea() const
nsIScrollableFrame *scrollableFrame = do_QueryFrame(GetParent());
if (scrollableFrame) {
nsRect portRect = scrollableFrame->GetScrollPortRect();
result.UnionRect(result, nsRect(nsPoint(0, 0), portRect.Size()));
}
return result;
}
+nsPopupSetFrame*
+nsCanvasFrame::GetPopupSetFrame()
+{
+ return mPopupSetFrame;
+}
+
+void
+nsCanvasFrame::SetPopupSetFrame(nsPopupSetFrame* aPopupSet)
+{
+ MOZ_ASSERT(!aPopupSet || !mPopupSetFrame,
+ "Popup set is already defined! Only 1 allowed.");
+ mPopupSetFrame = aPopupSet;
+}
+
+Element*
+nsCanvasFrame::GetDefaultTooltip()
+{
+ NS_WARNING("GetDefaultTooltip not implemented");
+ return nullptr;
+}
+
+void
+nsCanvasFrame::SetDefaultTooltip(Element* aTooltip)
+{
+ NS_WARNING("SetDefaultTooltip not implemented");
+ return;
+}
+
void
nsDisplayCanvasBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
nsPoint offset = ToReferenceFrame();
nsRect bgClipRect = frame->CanvasArea() + offset;
if (NS_GET_A(mColor) > 0) {
@@ -680,17 +736,17 @@ nsCanvasFrame::Reflow(nsPresContext*
// are placeholders for continuations of fixed-pos content, but those
// don't need to be reflowed. The normal child is always comes before
// the fixed-pos placeholders, because we insert it at the start
// of the child list, above.
ReflowOutput kidDesiredSize(aReflowInput);
if (mFrames.IsEmpty()) {
// We have no child frame, so return an empty size
aDesiredSize.Width() = aDesiredSize.Height() = 0;
- } else {
+ } else if (mFrames.FirstChild() != mPopupSetFrame) {
nsIFrame* kidFrame = mFrames.FirstChild();
bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0;
ReflowInput
kidReflowInput(aPresContext, aReflowInput, kidFrame,
aReflowInput.AvailableSize(kidFrame->GetWritingMode()));
if (aReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode()) &&
@@ -769,16 +825,31 @@ nsCanvasFrame::Reflow(nsPresContext*
}
if (prevCanvasFrame) {
ReflowOverflowContainerChildren(aPresContext, aReflowInput,
aDesiredSize.mOverflowAreas, 0,
aStatus);
}
+ if (mPopupSetFrame) {
+ MOZ_ASSERT(mFrames.ContainsFrame(mPopupSetFrame), "Only normal flow supported.");
+ nsReflowStatus popupStatus;
+ ReflowOutput popupDesiredSize(aReflowInput.GetWritingMode());
+ WritingMode wm = mPopupSetFrame->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(wm);
+ availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+ ReflowInput popupReflowInput(aPresContext, aReflowInput,
+ mPopupSetFrame, availSize);
+ ReflowChild(mPopupSetFrame, aPresContext, popupDesiredSize,
+ popupReflowInput, 0, 0, NS_FRAME_NO_MOVE_FRAME, popupStatus);
+ FinishReflowChild(mPopupSetFrame, aPresContext, popupDesiredSize,
+ &popupReflowInput, 0, 0, NS_FRAME_NO_MOVE_FRAME);
+ }
+
FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus);
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
nsresult
nsCanvasFrame::GetContentForEvent(WidgetEvent* aEvent, nsIContent** aContent)
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -8,46 +8,54 @@
#ifndef nsCanvasFrame_h___
#define nsCanvasFrame_h___
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "nsContainerFrame.h"
#include "nsIScrollPositionListener.h"
+#include "nsIRootBox.h"
#include "nsDisplayList.h"
#include "nsIAnonymousContentCreator.h"
#include "gfxPrefs.h"
class nsPresContext;
class gfxContext;
+class nsPopupSetFrame;
/**
* Root frame class.
*
* The root frame is the parent frame for the document element's frame.
* It only supports having a single child frame which must be an area
* frame.
* @note nsCanvasFrame keeps overflow container continuations of its child
* frame in the main child list.
*/
class nsCanvasFrame final : public nsContainerFrame,
public nsIScrollPositionListener,
- public nsIAnonymousContentCreator
+ public nsIAnonymousContentCreator,
+ public nsIRootBox
{
public:
explicit nsCanvasFrame(ComputedStyle* aStyle)
: nsContainerFrame(aStyle, kClassID)
, mDoPaintFocus(false)
, mAddedScrollPositionListener(false)
+ , mPopupSetFrame(nullptr)
{}
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS(nsCanvasFrame)
+ nsPopupSetFrame* GetPopupSetFrame() override;
+ void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) override;
+ Element* GetDefaultTooltip() override;
+ void SetDefaultTooltip(Element* aTooltip) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override;
virtual void SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList) override;
virtual void AppendFrames(ChildListID aListID,
nsFrameList& aFrameList) override;
virtual void InsertFrames(ChildListID aListID,
@@ -118,16 +126,20 @@ protected:
// 'this' and all its ancestors.
void MaybePropagateRootElementWritingMode();
// Data members
bool mDoPaintFocus;
bool mAddedScrollPositionListener;
nsCOMPtr<mozilla::dom::Element> mCustomContentContainer;
+
+private:
+ nsPopupSetFrame* mPopupSetFrame;
+ nsCOMPtr<mozilla::dom::Element> mPopupgroupContent;
};
/**
* Override nsDisplayBackground methods so that we pass aBGClipRect to
* PaintBackground, covering the whole overflow area.
* We can also paint an "extra background color" behind the normal
* background.
*/
--- a/layout/xul/nsRootBoxFrame.cpp
+++ b/layout/xul/nsRootBoxFrame.cpp
@@ -33,16 +33,25 @@ nsIRootBox::GetRootBox(nsIPresShell* aSh
return nullptr;
}
if (rootFrame) {
rootFrame = rootFrame->PrincipalChildList().FirstChild();
}
nsIRootBox* rootBox = do_QueryFrame(rootFrame);
+
+ // If the rootBox was not found yet this may be a top level non-XUL document.
+ if (rootFrame && !rootBox) {
+ // In a non-XUL document the rootFrame here will be a nsHTMLScrollFrame,
+ // get the nsCanvasFrame (which is the popup container) from it.
+ rootFrame = rootFrame->GetContentInsertionFrame();
+ rootBox = do_QueryFrame(rootFrame);
+ }
+
return rootBox;
}
class nsRootBoxFrame final : public nsBoxFrame, public nsIRootBox
{
public:
friend nsIFrame* NS_NewBoxFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle);
@@ -216,21 +225,19 @@ nsRootBoxFrame::SetPopupSetFrame(nsPopup
// Under normal conditions this should only be called once. However,
// if something triggers ReconstructDocElementHierarchy, we will
// destroy this frame's child (the nsDocElementBoxFrame), but not this
// frame. This will cause the popupset to remove itself by calling
// |SetPopupSetFrame(nullptr)|, and then we'll be able to accept a new
// popupset. Since the anonymous content is associated with the
// nsDocElementBoxFrame, we'll get a new popupset when the new doc
// element box frame is created.
- if (!mPopupSetFrame || !aPopupSet) {
- mPopupSetFrame = aPopupSet;
- } else {
- MOZ_ASSERT_UNREACHABLE("Popup set is already defined! Only 1 allowed.");
- }
+ MOZ_ASSERT(!aPopupSet || !mPopupSetFrame,
+ "Popup set is already defined! Only 1 allowed.");
+ mPopupSetFrame = aPopupSet;
}
Element*
nsRootBoxFrame::GetDefaultTooltip()
{
return mDefaultTooltip;
}