|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsHTMLParts.h" |
|
7 #include "nsStyleConsts.h" |
|
8 #include "nsGkAtoms.h" |
|
9 #include "nsIPresShell.h" |
|
10 #include "nsBoxFrame.h" |
|
11 #include "nsStackLayout.h" |
|
12 #include "nsIRootBox.h" |
|
13 #include "nsIContent.h" |
|
14 #include "nsXULTooltipListener.h" |
|
15 #include "nsFrameManager.h" |
|
16 #include "mozilla/BasicEvents.h" |
|
17 |
|
18 using namespace mozilla; |
|
19 |
|
20 // Interface IDs |
|
21 |
|
22 //#define DEBUG_REFLOW |
|
23 |
|
24 // static |
|
25 nsIRootBox* |
|
26 nsIRootBox::GetRootBox(nsIPresShell* aShell) |
|
27 { |
|
28 if (!aShell) { |
|
29 return nullptr; |
|
30 } |
|
31 nsIFrame* rootFrame = aShell->FrameManager()->GetRootFrame(); |
|
32 if (!rootFrame) { |
|
33 return nullptr; |
|
34 } |
|
35 |
|
36 if (rootFrame) { |
|
37 rootFrame = rootFrame->GetFirstPrincipalChild(); |
|
38 } |
|
39 |
|
40 nsIRootBox* rootBox = do_QueryFrame(rootFrame); |
|
41 return rootBox; |
|
42 } |
|
43 |
|
44 class nsRootBoxFrame : public nsBoxFrame, public nsIRootBox { |
|
45 public: |
|
46 |
|
47 friend nsIFrame* NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); |
|
48 |
|
49 nsRootBoxFrame(nsIPresShell* aShell, nsStyleContext *aContext); |
|
50 |
|
51 NS_DECL_QUERYFRAME |
|
52 NS_DECL_FRAMEARENA_HELPERS |
|
53 |
|
54 virtual nsPopupSetFrame* GetPopupSetFrame() MOZ_OVERRIDE; |
|
55 virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) MOZ_OVERRIDE; |
|
56 virtual nsIContent* GetDefaultTooltip() MOZ_OVERRIDE; |
|
57 virtual void SetDefaultTooltip(nsIContent* aTooltip) MOZ_OVERRIDE; |
|
58 virtual nsresult AddTooltipSupport(nsIContent* aNode) MOZ_OVERRIDE; |
|
59 virtual nsresult RemoveTooltipSupport(nsIContent* aNode) MOZ_OVERRIDE; |
|
60 |
|
61 virtual nsresult AppendFrames(ChildListID aListID, |
|
62 nsFrameList& aFrameList) MOZ_OVERRIDE; |
|
63 virtual nsresult InsertFrames(ChildListID aListID, |
|
64 nsIFrame* aPrevFrame, |
|
65 nsFrameList& aFrameList) MOZ_OVERRIDE; |
|
66 virtual nsresult RemoveFrame(ChildListID aListID, |
|
67 nsIFrame* aOldFrame) MOZ_OVERRIDE; |
|
68 |
|
69 virtual nsresult Reflow(nsPresContext* aPresContext, |
|
70 nsHTMLReflowMetrics& aDesiredSize, |
|
71 const nsHTMLReflowState& aReflowState, |
|
72 nsReflowStatus& aStatus) MOZ_OVERRIDE; |
|
73 virtual nsresult HandleEvent(nsPresContext* aPresContext, |
|
74 WidgetGUIEvent* aEvent, |
|
75 nsEventStatus* aEventStatus) MOZ_OVERRIDE; |
|
76 |
|
77 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
78 const nsRect& aDirtyRect, |
|
79 const nsDisplayListSet& aLists) MOZ_OVERRIDE; |
|
80 |
|
81 /** |
|
82 * Get the "type" of the frame |
|
83 * |
|
84 * @see nsGkAtoms::rootFrame |
|
85 */ |
|
86 virtual nsIAtom* GetType() const MOZ_OVERRIDE; |
|
87 |
|
88 virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE |
|
89 { |
|
90 // Override bogus IsFrameOfType in nsBoxFrame. |
|
91 if (aFlags & (nsIFrame::eReplacedContainsBlock | nsIFrame::eReplaced)) |
|
92 return false; |
|
93 return nsBoxFrame::IsFrameOfType(aFlags); |
|
94 } |
|
95 |
|
96 #ifdef DEBUG_FRAME_DUMP |
|
97 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; |
|
98 #endif |
|
99 |
|
100 nsPopupSetFrame* mPopupSetFrame; |
|
101 |
|
102 protected: |
|
103 nsIContent* mDefaultTooltip; |
|
104 }; |
|
105 |
|
106 //---------------------------------------------------------------------- |
|
107 |
|
108 nsIFrame* |
|
109 NS_NewRootBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
110 { |
|
111 return new (aPresShell) nsRootBoxFrame (aPresShell, aContext); |
|
112 } |
|
113 |
|
114 NS_IMPL_FRAMEARENA_HELPERS(nsRootBoxFrame) |
|
115 |
|
116 nsRootBoxFrame::nsRootBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext): |
|
117 nsBoxFrame(aShell, aContext, true) |
|
118 { |
|
119 mPopupSetFrame = nullptr; |
|
120 |
|
121 nsCOMPtr<nsBoxLayout> layout; |
|
122 NS_NewStackLayout(aShell, layout); |
|
123 SetLayoutManager(layout); |
|
124 } |
|
125 |
|
126 nsresult |
|
127 nsRootBoxFrame::AppendFrames(ChildListID aListID, |
|
128 nsFrameList& aFrameList) |
|
129 { |
|
130 nsresult rv; |
|
131 |
|
132 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list ID"); |
|
133 NS_PRECONDITION(mFrames.IsEmpty(), "already have a child frame"); |
|
134 if (aListID != kPrincipalList) { |
|
135 // We only support the principal child list. |
|
136 rv = NS_ERROR_INVALID_ARG; |
|
137 } else if (!mFrames.IsEmpty()) { |
|
138 // We only allow a single child frame. |
|
139 rv = NS_ERROR_FAILURE; |
|
140 } else { |
|
141 rv = nsBoxFrame::AppendFrames(aListID, aFrameList); |
|
142 } |
|
143 |
|
144 return rv; |
|
145 } |
|
146 |
|
147 nsresult |
|
148 nsRootBoxFrame::InsertFrames(ChildListID aListID, |
|
149 nsIFrame* aPrevFrame, |
|
150 nsFrameList& aFrameList) |
|
151 { |
|
152 nsresult rv; |
|
153 |
|
154 // Because we only support a single child frame inserting is the same |
|
155 // as appending. |
|
156 NS_PRECONDITION(!aPrevFrame, "unexpected previous sibling frame"); |
|
157 if (aPrevFrame) { |
|
158 rv = NS_ERROR_UNEXPECTED; |
|
159 } else { |
|
160 rv = AppendFrames(aListID, aFrameList); |
|
161 } |
|
162 |
|
163 return rv; |
|
164 } |
|
165 |
|
166 nsresult |
|
167 nsRootBoxFrame::RemoveFrame(ChildListID aListID, |
|
168 nsIFrame* aOldFrame) |
|
169 { |
|
170 nsresult rv; |
|
171 |
|
172 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list ID"); |
|
173 if (aListID != kPrincipalList) { |
|
174 // We only support the principal child list. |
|
175 rv = NS_ERROR_INVALID_ARG; |
|
176 } else if (aOldFrame == mFrames.FirstChild()) { |
|
177 rv = nsBoxFrame::RemoveFrame(aListID, aOldFrame); |
|
178 } else { |
|
179 rv = NS_ERROR_FAILURE; |
|
180 } |
|
181 |
|
182 return rv; |
|
183 } |
|
184 |
|
185 #ifdef DEBUG_REFLOW |
|
186 int32_t gReflows = 0; |
|
187 #endif |
|
188 |
|
189 nsresult |
|
190 nsRootBoxFrame::Reflow(nsPresContext* aPresContext, |
|
191 nsHTMLReflowMetrics& aDesiredSize, |
|
192 const nsHTMLReflowState& aReflowState, |
|
193 nsReflowStatus& aStatus) |
|
194 { |
|
195 DO_GLOBAL_REFLOW_COUNT("nsRootBoxFrame"); |
|
196 |
|
197 #ifdef DEBUG_REFLOW |
|
198 gReflows++; |
|
199 printf("----Reflow %d----\n", gReflows); |
|
200 #endif |
|
201 return nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); |
|
202 } |
|
203 |
|
204 void |
|
205 nsRootBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
206 const nsRect& aDirtyRect, |
|
207 const nsDisplayListSet& aLists) |
|
208 { |
|
209 // root boxes don't need a debug border/outline or a selection overlay... |
|
210 // They *may* have a background propagated to them, so force creation |
|
211 // of a background display list element. |
|
212 DisplayBorderBackgroundOutline(aBuilder, aLists, true); |
|
213 |
|
214 BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists); |
|
215 } |
|
216 |
|
217 nsresult |
|
218 nsRootBoxFrame::HandleEvent(nsPresContext* aPresContext, |
|
219 WidgetGUIEvent* aEvent, |
|
220 nsEventStatus* aEventStatus) |
|
221 { |
|
222 NS_ENSURE_ARG_POINTER(aEventStatus); |
|
223 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { |
|
224 return NS_OK; |
|
225 } |
|
226 |
|
227 if (aEvent->message == NS_MOUSE_BUTTON_UP) { |
|
228 nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus); |
|
229 } |
|
230 |
|
231 return NS_OK; |
|
232 } |
|
233 |
|
234 // REVIEW: The override here was doing nothing since nsBoxFrame is our |
|
235 // parent class |
|
236 nsIAtom* |
|
237 nsRootBoxFrame::GetType() const |
|
238 { |
|
239 return nsGkAtoms::rootFrame; |
|
240 } |
|
241 |
|
242 nsPopupSetFrame* |
|
243 nsRootBoxFrame::GetPopupSetFrame() |
|
244 { |
|
245 return mPopupSetFrame; |
|
246 } |
|
247 |
|
248 void |
|
249 nsRootBoxFrame::SetPopupSetFrame(nsPopupSetFrame* aPopupSet) |
|
250 { |
|
251 // Under normal conditions this should only be called once. However, |
|
252 // if something triggers ReconstructDocElementHierarchy, we will |
|
253 // destroy this frame's child (the nsDocElementBoxFrame), but not this |
|
254 // frame. This will cause the popupset to remove itself by calling |
|
255 // |SetPopupSetFrame(nullptr)|, and then we'll be able to accept a new |
|
256 // popupset. Since the anonymous content is associated with the |
|
257 // nsDocElementBoxFrame, we'll get a new popupset when the new doc |
|
258 // element box frame is created. |
|
259 if (!mPopupSetFrame || !aPopupSet) { |
|
260 mPopupSetFrame = aPopupSet; |
|
261 } else { |
|
262 NS_NOTREACHED("Popup set is already defined! Only 1 allowed."); |
|
263 } |
|
264 } |
|
265 |
|
266 nsIContent* |
|
267 nsRootBoxFrame::GetDefaultTooltip() |
|
268 { |
|
269 return mDefaultTooltip; |
|
270 } |
|
271 |
|
272 void |
|
273 nsRootBoxFrame::SetDefaultTooltip(nsIContent* aTooltip) |
|
274 { |
|
275 mDefaultTooltip = aTooltip; |
|
276 } |
|
277 |
|
278 nsresult |
|
279 nsRootBoxFrame::AddTooltipSupport(nsIContent* aNode) |
|
280 { |
|
281 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); |
|
282 |
|
283 nsXULTooltipListener *listener = nsXULTooltipListener::GetInstance(); |
|
284 if (!listener) |
|
285 return NS_ERROR_OUT_OF_MEMORY; |
|
286 |
|
287 return listener->AddTooltipSupport(aNode); |
|
288 } |
|
289 |
|
290 nsresult |
|
291 nsRootBoxFrame::RemoveTooltipSupport(nsIContent* aNode) |
|
292 { |
|
293 // XXjh yuck, I'll have to implement a way to get at |
|
294 // the tooltip listener for a given node to make |
|
295 // this work. Not crucial, we aren't removing |
|
296 // tooltips from any nodes in the app just yet. |
|
297 return NS_ERROR_NOT_IMPLEMENTED; |
|
298 } |
|
299 |
|
300 NS_QUERYFRAME_HEAD(nsRootBoxFrame) |
|
301 NS_QUERYFRAME_ENTRY(nsIRootBox) |
|
302 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame) |
|
303 |
|
304 #ifdef DEBUG_FRAME_DUMP |
|
305 nsresult |
|
306 nsRootBoxFrame::GetFrameName(nsAString& aResult) const |
|
307 { |
|
308 return MakeFrameName(NS_LITERAL_STRING("RootBox"), aResult); |
|
309 } |
|
310 #endif |