1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 #include "nsHTMLParts.h"
8 #include "nsStyleConsts.h"
9 #include "nsGkAtoms.h"
10 #include "nsBoxFrame.h"
11 #include "nsDisplayList.h"
12 #include "nsStackLayout.h"
13 #include "nsIPopupContainer.h"
14 #include "nsIContent.h"
15 #include "nsFrameManager.h"
16 #include "nsLayoutUtils.h"
17 #include "mozilla/BasicEvents.h"
18 #include "mozilla/DisplayPortUtils.h"
19 #include "mozilla/PresShell.h"
20 
21 using namespace mozilla;
22 
23 // Interface IDs
24 
25 // static
GetPopupContainer(PresShell * aPresShell)26 nsIPopupContainer* nsIPopupContainer::GetPopupContainer(PresShell* aPresShell) {
27   if (!aPresShell) {
28     return nullptr;
29   }
30   nsIFrame* rootFrame = aPresShell->GetRootFrame();
31   if (!rootFrame) {
32     return nullptr;
33   }
34 
35   if (rootFrame) {
36     rootFrame = rootFrame->PrincipalChildList().FirstChild();
37   }
38 
39   nsIPopupContainer* rootBox = do_QueryFrame(rootFrame);
40 
41   // If the rootBox was not found yet this may be a top level non-XUL document.
42   if (rootFrame && !rootBox) {
43     // In a non-XUL document the rootFrame here will be a nsHTMLScrollFrame,
44     // get the nsCanvasFrame (which is the popup container) from it.
45     rootFrame = rootFrame->GetContentInsertionFrame();
46     rootBox = do_QueryFrame(rootFrame);
47   }
48 
49   return rootBox;
50 }
51 
52 class nsRootBoxFrame final : public nsBoxFrame, public nsIPopupContainer {
53  public:
54   friend nsIFrame* NS_NewBoxFrame(mozilla::PresShell* aPresShell,
55                                   ComputedStyle* aStyle);
56 
57   explicit nsRootBoxFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
58 
59   NS_DECL_QUERYFRAME
60   NS_DECL_FRAMEARENA_HELPERS(nsRootBoxFrame)
61 
62   virtual nsPopupSetFrame* GetPopupSetFrame() override;
63   virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) override;
64   virtual dom::Element* GetDefaultTooltip() override;
65   virtual void SetDefaultTooltip(dom::Element* aTooltip) override;
66 
67   virtual void AppendFrames(ChildListID aListID,
68                             nsFrameList& aFrameList) override;
69   virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
70                             const nsLineList::iterator* aPrevFrameLine,
71                             nsFrameList& aFrameList) override;
72   virtual void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
73 
74   virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
75                       const ReflowInput& aReflowInput,
76                       nsReflowStatus& aStatus) override;
77   virtual nsresult HandleEvent(nsPresContext* aPresContext,
78                                WidgetGUIEvent* aEvent,
79                                nsEventStatus* aEventStatus) override;
80 
81   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
82                                 const nsDisplayListSet& aLists) override;
83 
IsFrameOfType(uint32_t aFlags) const84   virtual bool IsFrameOfType(uint32_t aFlags) const override {
85     // Override bogus IsFrameOfType in nsBoxFrame.
86     if (aFlags & (nsIFrame::eReplacedContainsBlock | nsIFrame::eReplaced))
87       return false;
88     return nsBoxFrame::IsFrameOfType(aFlags);
89   }
90 
91 #ifdef DEBUG_FRAME_DUMP
92   virtual nsresult GetFrameName(nsAString& aResult) const override;
93 #endif
94 
95   nsPopupSetFrame* mPopupSetFrame;
96 
97  protected:
98   dom::Element* mDefaultTooltip;
99 };
100 
101 //----------------------------------------------------------------------
102 
NS_NewRootBoxFrame(PresShell * aPresShell,ComputedStyle * aStyle)103 nsContainerFrame* NS_NewRootBoxFrame(PresShell* aPresShell,
104                                      ComputedStyle* aStyle) {
105   return new (aPresShell) nsRootBoxFrame(aStyle, aPresShell->GetPresContext());
106 }
107 
NS_IMPL_FRAMEARENA_HELPERS(nsRootBoxFrame)108 NS_IMPL_FRAMEARENA_HELPERS(nsRootBoxFrame)
109 
110 nsRootBoxFrame::nsRootBoxFrame(ComputedStyle* aStyle,
111                                nsPresContext* aPresContext)
112     : nsBoxFrame(aStyle, aPresContext, kClassID, true),
113       mPopupSetFrame(nullptr),
114       mDefaultTooltip(nullptr) {
115   nsCOMPtr<nsBoxLayout> layout;
116   NS_NewStackLayout(layout);
117   SetXULLayoutManager(layout);
118 }
119 
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)120 void nsRootBoxFrame::AppendFrames(ChildListID aListID,
121                                   nsFrameList& aFrameList) {
122   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list ID");
123   MOZ_ASSERT(mFrames.IsEmpty(), "already have a child frame");
124   nsBoxFrame::AppendFrames(aListID, aFrameList);
125 }
126 
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aFrameList)127 void nsRootBoxFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
128                                   const nsLineList::iterator* aPrevFrameLine,
129                                   nsFrameList& aFrameList) {
130   // Because we only support a single child frame inserting is the same
131   // as appending.
132   MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
133   AppendFrames(aListID, aFrameList);
134 }
135 
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)136 void nsRootBoxFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) {
137   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list ID");
138   if (aOldFrame == mFrames.FirstChild()) {
139     nsBoxFrame::RemoveFrame(aListID, aOldFrame);
140   } else {
141     MOZ_CRASH("unknown aOldFrame");
142   }
143 }
144 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)145 void nsRootBoxFrame::Reflow(nsPresContext* aPresContext,
146                             ReflowOutput& aDesiredSize,
147                             const ReflowInput& aReflowInput,
148                             nsReflowStatus& aStatus) {
149   DO_GLOBAL_REFLOW_COUNT("nsRootBoxFrame");
150   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
151 
152   return nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
153 }
154 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)155 void nsRootBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
156                                       const nsDisplayListSet& aLists) {
157   if (mContent && mContent->GetProperty(nsGkAtoms::DisplayPortMargins)) {
158     // The XUL document's root element may have displayport margins set in
159     // ChromeProcessController::InitializeRoot, and we should to supply the
160     // base rect.
161     nsRect displayPortBase =
162         aBuilder->GetVisibleRect().Intersect(nsRect(nsPoint(0, 0), GetSize()));
163     DisplayPortUtils::SetDisplayPortBase(mContent, displayPortBase);
164   }
165 
166   // root boxes don't need a debug border/outline or a selection overlay...
167   // They *may* have a background propagated to them, so force creation
168   // of a background display list element.
169   DisplayBorderBackgroundOutline(aBuilder, aLists, true);
170 
171   BuildDisplayListForChildren(aBuilder, aLists);
172 }
173 
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)174 nsresult nsRootBoxFrame::HandleEvent(nsPresContext* aPresContext,
175                                      WidgetGUIEvent* aEvent,
176                                      nsEventStatus* aEventStatus) {
177   NS_ENSURE_ARG_POINTER(aEventStatus);
178   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
179     return NS_OK;
180   }
181 
182   if (aEvent->mMessage == eMouseUp) {
183     nsIFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
184   }
185 
186   return NS_OK;
187 }
188 
GetPopupSetFrame()189 nsPopupSetFrame* nsRootBoxFrame::GetPopupSetFrame() { return mPopupSetFrame; }
190 
SetPopupSetFrame(nsPopupSetFrame * aPopupSet)191 void nsRootBoxFrame::SetPopupSetFrame(nsPopupSetFrame* aPopupSet) {
192   // Under normal conditions this should only be called once.  However,
193   // if something triggers ReconstructDocElementHierarchy, we will
194   // destroy this frame's child (the nsDocElementBoxFrame), but not this
195   // frame.  This will cause the popupset to remove itself by calling
196   // |SetPopupSetFrame(nullptr)|, and then we'll be able to accept a new
197   // popupset.  Since the anonymous content is associated with the
198   // nsDocElementBoxFrame, we'll get a new popupset when the new doc
199   // element box frame is created.
200   MOZ_ASSERT(!aPopupSet || !mPopupSetFrame,
201              "Popup set is already defined! Only 1 allowed.");
202   mPopupSetFrame = aPopupSet;
203 }
204 
GetDefaultTooltip()205 dom::Element* nsRootBoxFrame::GetDefaultTooltip() { return mDefaultTooltip; }
206 
SetDefaultTooltip(dom::Element * aTooltip)207 void nsRootBoxFrame::SetDefaultTooltip(dom::Element* aTooltip) {
208   mDefaultTooltip = aTooltip;
209 }
210 
211 NS_QUERYFRAME_HEAD(nsRootBoxFrame)
NS_QUERYFRAME_ENTRY(nsIPopupContainer)212   NS_QUERYFRAME_ENTRY(nsIPopupContainer)
213 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
214 
215 #ifdef DEBUG_FRAME_DUMP
216 nsresult nsRootBoxFrame::GetFrameName(nsAString& aResult) const {
217   return MakeFrameName(u"RootBox"_ns, aResult);
218 }
219 #endif
220