Bug 1466897 - Support context menus in top level chrome privileged HTML pages. r=mats draft
authorBrendan Dahl <brendan.dahl@gmail.com>
Mon, 04 Jun 2018 18:02:32 -0700
changeset 814585 0d302ef0d1dca886db05ed78bf6d981c569f14df
parent 814584 e8b7331398910233e3adaaed01cad6b75bb562a2
child 814586 13210c774e66b3153f92a789bc9e6d2ad3f89b67
push id115269
push userbmo:bdahl@mozilla.com
push dateThu, 05 Jul 2018 17:57:49 +0000
reviewersmats
bugs1466897
milestone63.0a1
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
layout/generic/nsCanvasFrame.cpp
layout/generic/nsCanvasFrame.h
layout/xul/nsRootBoxFrame.cpp
--- 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;
 }