/src/mozilla-central/layout/xul/nsBoxFrame.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | // |
8 | | // Eric Vaughan |
9 | | // Netscape Communications |
10 | | // |
11 | | // See documentation in associated header file |
12 | | // |
13 | | |
14 | | // How boxes layout |
15 | | // ---------------- |
16 | | // Boxes layout a bit differently than html. html does a bottom up layout. Where boxes do a top down. |
17 | | // 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes. |
18 | | // 2) It then adds them up to determine its size. |
19 | | // 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size |
20 | | // otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if |
21 | | // Necessary. |
22 | | // |
23 | | // However there is a catch. Some html components like block frames can not determine their preferred size. |
24 | | // this is their size if they were laid out intrinsically. So the box will flow the child to determine this can |
25 | | // cache the value. |
26 | | |
27 | | // Boxes and Incremental Reflow |
28 | | // ---------------------------- |
29 | | // Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental |
30 | | // reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change |
31 | | // any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared |
32 | | // so when asked for there current size they can relayout themselves. |
33 | | |
34 | | #include "nsBoxFrame.h" |
35 | | |
36 | | #include "gfxUtils.h" |
37 | | #include "mozilla/gfx/2D.h" |
38 | | #include "nsBoxLayoutState.h" |
39 | | #include "mozilla/dom/Touch.h" |
40 | | #include "mozilla/Move.h" |
41 | | #include "mozilla/ComputedStyle.h" |
42 | | #include "nsPlaceholderFrame.h" |
43 | | #include "nsPresContext.h" |
44 | | #include "nsCOMPtr.h" |
45 | | #include "nsNameSpaceManager.h" |
46 | | #include "nsGkAtoms.h" |
47 | | #include "nsIContent.h" |
48 | | #include "nsHTMLParts.h" |
49 | | #include "nsViewManager.h" |
50 | | #include "nsView.h" |
51 | | #include "nsIPresShell.h" |
52 | | #include "nsCSSRendering.h" |
53 | | #include "nsIServiceManager.h" |
54 | | #include "nsBoxLayout.h" |
55 | | #include "nsSprocketLayout.h" |
56 | | #include "nsIScrollableFrame.h" |
57 | | #include "nsWidgetsCID.h" |
58 | | #include "nsCSSAnonBoxes.h" |
59 | | #include "nsContainerFrame.h" |
60 | | #include "nsITheme.h" |
61 | | #include "nsTransform2D.h" |
62 | | #include "mozilla/EventStateManager.h" |
63 | | #include "nsDisplayList.h" |
64 | | #include "mozilla/Preferences.h" |
65 | | #include "nsStyleConsts.h" |
66 | | #include "nsLayoutUtils.h" |
67 | | #include "nsSliderFrame.h" |
68 | | #include <algorithm> |
69 | | |
70 | | // Needed for Print Preview |
71 | | #include "nsIURI.h" |
72 | | |
73 | | #include "mozilla/TouchEvents.h" |
74 | | |
75 | | using namespace mozilla; |
76 | | using namespace mozilla::dom; |
77 | | using namespace mozilla::gfx; |
78 | | |
79 | | nsIFrame* |
80 | | NS_NewBoxFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle, bool aIsRoot, nsBoxLayout* aLayoutManager) |
81 | 0 | { |
82 | 0 | return new (aPresShell) nsBoxFrame(aStyle, nsBoxFrame::kClassID, |
83 | 0 | aIsRoot, aLayoutManager); |
84 | 0 | } |
85 | | |
86 | | nsIFrame* |
87 | | NS_NewBoxFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
88 | 0 | { |
89 | 0 | return new (aPresShell) nsBoxFrame(aStyle); |
90 | 0 | } |
91 | | |
92 | | NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame) |
93 | | |
94 | | #ifdef DEBUG |
95 | | NS_QUERYFRAME_HEAD(nsBoxFrame) |
96 | | NS_QUERYFRAME_ENTRY(nsBoxFrame) |
97 | | NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
98 | | #endif |
99 | | |
100 | | nsBoxFrame::nsBoxFrame(ComputedStyle* aStyle, |
101 | | ClassID aID, |
102 | | bool aIsRoot, |
103 | | nsBoxLayout* aLayoutManager) |
104 | | : nsContainerFrame(aStyle, aID) |
105 | | , mFlex(0) |
106 | | , mAscent(0) |
107 | 0 | { |
108 | 0 | AddStateBits(NS_STATE_IS_HORIZONTAL | NS_STATE_AUTO_STRETCH); |
109 | 0 |
|
110 | 0 | if (aIsRoot) |
111 | 0 | AddStateBits(NS_STATE_IS_ROOT); |
112 | 0 |
|
113 | 0 | mValign = vAlign_Top; |
114 | 0 | mHalign = hAlign_Left; |
115 | 0 |
|
116 | 0 | // if no layout manager specified us the static sprocket layout |
117 | 0 | nsCOMPtr<nsBoxLayout> layout = aLayoutManager; |
118 | 0 |
|
119 | 0 | if (layout == nullptr) { |
120 | 0 | NS_NewSprocketLayout(layout); |
121 | 0 | } |
122 | 0 |
|
123 | 0 | SetXULLayoutManager(layout); |
124 | 0 | } |
125 | | |
126 | | nsBoxFrame::~nsBoxFrame() |
127 | 0 | { |
128 | 0 | } |
129 | | |
130 | | void |
131 | | nsBoxFrame::SetInitialChildList(ChildListID aListID, |
132 | | nsFrameList& aChildList) |
133 | 0 | { |
134 | 0 | nsContainerFrame::SetInitialChildList(aListID, aChildList); |
135 | 0 | if (aListID == kPrincipalList) { |
136 | 0 | // initialize our list of infos. |
137 | 0 | nsBoxLayoutState state(PresContext()); |
138 | 0 | CheckBoxOrder(); |
139 | 0 | if (mLayoutManager) |
140 | 0 | mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild()); |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | /* virtual */ void |
145 | | nsBoxFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) |
146 | 0 | { |
147 | 0 | nsContainerFrame::DidSetComputedStyle(aOldComputedStyle); |
148 | 0 |
|
149 | 0 | // The values that CacheAttributes() computes depend on our style, |
150 | 0 | // so we need to recompute them here... |
151 | 0 | CacheAttributes(); |
152 | 0 | } |
153 | | |
154 | | /** |
155 | | * Initialize us. This is a good time to get the alignment of the box |
156 | | */ |
157 | | void |
158 | | nsBoxFrame::Init(nsIContent* aContent, |
159 | | nsContainerFrame* aParent, |
160 | | nsIFrame* aPrevInFlow) |
161 | 0 | { |
162 | 0 | nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
163 | 0 |
|
164 | 0 | if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) { |
165 | 0 | AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
166 | 0 | } |
167 | 0 |
|
168 | 0 | MarkIntrinsicISizesDirty(); |
169 | 0 |
|
170 | 0 | CacheAttributes(); |
171 | 0 |
|
172 | 0 | UpdateMouseThrough(); |
173 | 0 |
|
174 | 0 | // register access key |
175 | 0 | RegUnregAccessKey(true); |
176 | 0 | } |
177 | | |
178 | | void nsBoxFrame::UpdateMouseThrough() |
179 | | { |
180 | | static Element::AttrValuesArray strings[] = |
181 | | {&nsGkAtoms::never, &nsGkAtoms::always, nullptr}; |
182 | | switch (mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, |
183 | | nsGkAtoms::mousethrough, strings, eCaseMatters)) { |
184 | | case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break; |
185 | | case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break; |
186 | | case 2: { |
187 | | RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); |
188 | | RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); |
189 | | break; |
190 | | } |
191 | | } |
192 | | } |
193 | | |
194 | | void |
195 | | nsBoxFrame::CacheAttributes() |
196 | 0 | { |
197 | 0 | /* |
198 | 0 | printf("Caching: "); |
199 | 0 | XULDumpBox(stdout); |
200 | 0 | printf("\n"); |
201 | 0 | */ |
202 | 0 |
|
203 | 0 | mValign = vAlign_Top; |
204 | 0 | mHalign = hAlign_Left; |
205 | 0 |
|
206 | 0 | bool orient = false; |
207 | 0 | GetInitialOrientation(orient); |
208 | 0 | if (orient) |
209 | 0 | AddStateBits(NS_STATE_IS_HORIZONTAL); |
210 | 0 | else |
211 | 0 | RemoveStateBits(NS_STATE_IS_HORIZONTAL); |
212 | 0 |
|
213 | 0 | bool normal = true; |
214 | 0 | GetInitialDirection(normal); |
215 | 0 | if (normal) |
216 | 0 | AddStateBits(NS_STATE_IS_DIRECTION_NORMAL); |
217 | 0 | else |
218 | 0 | RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL); |
219 | 0 |
|
220 | 0 | GetInitialVAlignment(mValign); |
221 | 0 | GetInitialHAlignment(mHalign); |
222 | 0 |
|
223 | 0 | bool equalSize = false; |
224 | 0 | GetInitialEqualSize(equalSize); |
225 | 0 | if (equalSize) |
226 | 0 | AddStateBits(NS_STATE_EQUAL_SIZE); |
227 | 0 | else |
228 | 0 | RemoveStateBits(NS_STATE_EQUAL_SIZE); |
229 | 0 |
|
230 | 0 | bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH); |
231 | 0 | GetInitialAutoStretch(autostretch); |
232 | 0 | if (autostretch) |
233 | 0 | AddStateBits(NS_STATE_AUTO_STRETCH); |
234 | 0 | else |
235 | 0 | RemoveStateBits(NS_STATE_AUTO_STRETCH); |
236 | 0 | } |
237 | | |
238 | | bool |
239 | | nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign) |
240 | 0 | { |
241 | 0 | if (!GetContent() || !GetContent()->IsElement()) |
242 | 0 | return false; |
243 | 0 | |
244 | 0 | Element* element = GetContent()->AsElement(); |
245 | 0 | // XXXdwh Everything inside this if statement is deprecated code. |
246 | 0 | static Element::AttrValuesArray alignStrings[] = |
247 | 0 | {&nsGkAtoms::left, &nsGkAtoms::right, nullptr}; |
248 | 0 | static const Halignment alignValues[] = {hAlign_Left, hAlign_Right}; |
249 | 0 | int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align, |
250 | 0 | alignStrings, eCaseMatters); |
251 | 0 | if (index >= 0) { |
252 | 0 | aHalign = alignValues[index]; |
253 | 0 | return true; |
254 | 0 | } |
255 | 0 | |
256 | 0 | // Now that the deprecated stuff is out of the way, we move on to check the appropriate |
257 | 0 | // attribute. For horizontal boxes, we are checking the PACK attribute. For vertical boxes |
258 | 0 | // we are checking the ALIGN attribute. |
259 | 0 | nsAtom* attrName = IsXULHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align; |
260 | 0 | static Element::AttrValuesArray strings[] = |
261 | 0 | {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr}; |
262 | 0 | static const Halignment values[] = |
263 | 0 | {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right}; |
264 | 0 | index = element->FindAttrValueIn(kNameSpaceID_None, attrName, |
265 | 0 | strings, eCaseMatters); |
266 | 0 |
|
267 | 0 | if (index == Element::ATTR_VALUE_NO_MATCH) { |
268 | 0 | // The attr was present but had a nonsensical value. Revert to the default. |
269 | 0 | return false; |
270 | 0 | } |
271 | 0 | if (index > 0) { |
272 | 0 | aHalign = values[index]; |
273 | 0 | return true; |
274 | 0 | } |
275 | 0 | |
276 | 0 | // Now that we've checked for the attribute it's time to check CSS. For |
277 | 0 | // horizontal boxes we're checking PACK. For vertical boxes we are checking |
278 | 0 | // ALIGN. |
279 | 0 | const nsStyleXUL* boxInfo = StyleXUL(); |
280 | 0 | if (IsXULHorizontal()) { |
281 | 0 | switch (boxInfo->mBoxPack) { |
282 | 0 | case StyleBoxPack::Start: |
283 | 0 | aHalign = nsBoxFrame::hAlign_Left; |
284 | 0 | return true; |
285 | 0 | case StyleBoxPack::Center: |
286 | 0 | aHalign = nsBoxFrame::hAlign_Center; |
287 | 0 | return true; |
288 | 0 | case StyleBoxPack::End: |
289 | 0 | aHalign = nsBoxFrame::hAlign_Right; |
290 | 0 | return true; |
291 | 0 | default: // Nonsensical value. Just bail. |
292 | 0 | return false; |
293 | 0 | } |
294 | 0 | } |
295 | 0 | else { |
296 | 0 | switch (boxInfo->mBoxAlign) { |
297 | 0 | case StyleBoxAlign::Start: |
298 | 0 | aHalign = nsBoxFrame::hAlign_Left; |
299 | 0 | return true; |
300 | 0 | case StyleBoxAlign::Center: |
301 | 0 | aHalign = nsBoxFrame::hAlign_Center; |
302 | 0 | return true; |
303 | 0 | case StyleBoxAlign::End: |
304 | 0 | aHalign = nsBoxFrame::hAlign_Right; |
305 | 0 | return true; |
306 | 0 | default: // Nonsensical value. Just bail. |
307 | 0 | return false; |
308 | 0 | } |
309 | 0 | } |
310 | 0 | |
311 | 0 | return false; |
312 | 0 | } |
313 | | |
314 | | bool |
315 | | nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign) |
316 | 0 | { |
317 | 0 | if (!GetContent() || !GetContent()->IsElement()) |
318 | 0 | return false; |
319 | 0 | |
320 | 0 | Element* element = GetContent()->AsElement(); |
321 | 0 |
|
322 | 0 | static Element::AttrValuesArray valignStrings[] = |
323 | 0 | {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr}; |
324 | 0 | static const Valignment valignValues[] = |
325 | 0 | {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom}; |
326 | 0 | int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign, |
327 | 0 | valignStrings, eCaseMatters); |
328 | 0 | if (index >= 0) { |
329 | 0 | aValign = valignValues[index]; |
330 | 0 | return true; |
331 | 0 | } |
332 | 0 | |
333 | 0 | // Now that the deprecated stuff is out of the way, we move on to check the appropriate |
334 | 0 | // attribute. For horizontal boxes, we are checking the ALIGN attribute. For vertical boxes |
335 | 0 | // we are checking the PACK attribute. |
336 | 0 | nsAtom* attrName = IsXULHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack; |
337 | 0 | static Element::AttrValuesArray strings[] = |
338 | 0 | {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, |
339 | 0 | &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr}; |
340 | 0 | static const Valignment values[] = |
341 | 0 | {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom}; |
342 | 0 | index = element->FindAttrValueIn(kNameSpaceID_None, attrName, |
343 | 0 | strings, eCaseMatters); |
344 | 0 | if (index == Element::ATTR_VALUE_NO_MATCH) { |
345 | 0 | // The attr was present but had a nonsensical value. Revert to the default. |
346 | 0 | return false; |
347 | 0 | } |
348 | 0 | if (index > 0) { |
349 | 0 | aValign = values[index]; |
350 | 0 | return true; |
351 | 0 | } |
352 | 0 | |
353 | 0 | // Now that we've checked for the attribute it's time to check CSS. For |
354 | 0 | // horizontal boxes we're checking ALIGN. For vertical boxes we are checking |
355 | 0 | // PACK. |
356 | 0 | const nsStyleXUL* boxInfo = StyleXUL(); |
357 | 0 | if (IsXULHorizontal()) { |
358 | 0 | switch (boxInfo->mBoxAlign) { |
359 | 0 | case StyleBoxAlign::Start: |
360 | 0 | aValign = nsBoxFrame::vAlign_Top; |
361 | 0 | return true; |
362 | 0 | case StyleBoxAlign::Center: |
363 | 0 | aValign = nsBoxFrame::vAlign_Middle; |
364 | 0 | return true; |
365 | 0 | case StyleBoxAlign::Baseline: |
366 | 0 | aValign = nsBoxFrame::vAlign_BaseLine; |
367 | 0 | return true; |
368 | 0 | case StyleBoxAlign::End: |
369 | 0 | aValign = nsBoxFrame::vAlign_Bottom; |
370 | 0 | return true; |
371 | 0 | default: // Nonsensical value. Just bail. |
372 | 0 | return false; |
373 | 0 | } |
374 | 0 | } |
375 | 0 | else { |
376 | 0 | switch (boxInfo->mBoxPack) { |
377 | 0 | case StyleBoxPack::Start: |
378 | 0 | aValign = nsBoxFrame::vAlign_Top; |
379 | 0 | return true; |
380 | 0 | case StyleBoxPack::Center: |
381 | 0 | aValign = nsBoxFrame::vAlign_Middle; |
382 | 0 | return true; |
383 | 0 | case StyleBoxPack::End: |
384 | 0 | aValign = nsBoxFrame::vAlign_Bottom; |
385 | 0 | return true; |
386 | 0 | default: // Nonsensical value. Just bail. |
387 | 0 | return false; |
388 | 0 | } |
389 | 0 | } |
390 | 0 | |
391 | 0 | return false; |
392 | 0 | } |
393 | | |
394 | | void |
395 | | nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal) |
396 | 0 | { |
397 | 0 | // see if we are a vertical or horizontal box. |
398 | 0 | if (!GetContent()) |
399 | 0 | return; |
400 | 0 | |
401 | 0 | // Check the style system first. |
402 | 0 | const nsStyleXUL* boxInfo = StyleXUL(); |
403 | 0 | if (boxInfo->mBoxOrient == StyleBoxOrient::Horizontal) { |
404 | 0 | aIsHorizontal = true; |
405 | 0 | } else { |
406 | 0 | aIsHorizontal = false; |
407 | 0 | } |
408 | 0 |
|
409 | 0 | // Now see if we have an attribute. The attribute overrides |
410 | 0 | // the style system value. |
411 | 0 | if (!GetContent()->IsElement()) |
412 | 0 | return; |
413 | 0 | |
414 | 0 | static Element::AttrValuesArray strings[] = |
415 | 0 | {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr}; |
416 | 0 | int32_t index = |
417 | 0 | GetContent()->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient, |
418 | 0 | strings, eCaseMatters); |
419 | 0 | if (index >= 0) { |
420 | 0 | aIsHorizontal = index == 1; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | void |
425 | | nsBoxFrame::GetInitialDirection(bool& aIsNormal) |
426 | 0 | { |
427 | 0 | if (!GetContent()) |
428 | 0 | return; |
429 | 0 | |
430 | 0 | if (IsXULHorizontal()) { |
431 | 0 | // For horizontal boxes only, we initialize our value based off the CSS 'direction' property. |
432 | 0 | // This means that BiDI users will end up with horizontally inverted chrome. |
433 | 0 | aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we. |
434 | 0 | } |
435 | 0 | else |
436 | 0 | aIsNormal = true; // Assume a normal direction in the vertical case. |
437 | 0 |
|
438 | 0 | // Now check the style system to see if we should invert aIsNormal. |
439 | 0 | const nsStyleXUL* boxInfo = StyleXUL(); |
440 | 0 | if (boxInfo->mBoxDirection == StyleBoxDirection::Reverse) { |
441 | 0 | aIsNormal = !aIsNormal; // Invert our direction. |
442 | 0 | } |
443 | 0 |
|
444 | 0 | if (!GetContent()->IsElement()) { |
445 | 0 | return; |
446 | 0 | } |
447 | 0 | |
448 | 0 | Element* element = GetContent()->AsElement(); |
449 | 0 |
|
450 | 0 | // Now see if we have an attribute. The attribute overrides |
451 | 0 | // the style system value. |
452 | 0 | if (IsXULHorizontal()) { |
453 | 0 | static Element::AttrValuesArray strings[] = |
454 | 0 | {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr}; |
455 | 0 | int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir, |
456 | 0 | strings, eCaseMatters); |
457 | 0 | if (index >= 0) { |
458 | 0 | bool values[] = {!aIsNormal, true, false}; |
459 | 0 | aIsNormal = values[index]; |
460 | 0 | } |
461 | 0 | } else if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, |
462 | 0 | nsGkAtoms::reverse, eCaseMatters)) { |
463 | 0 | aIsNormal = !aIsNormal; |
464 | 0 | } |
465 | 0 | } |
466 | | |
467 | | /* Returns true if it was set. |
468 | | */ |
469 | | bool |
470 | | nsBoxFrame::GetInitialEqualSize(bool& aEqualSize) |
471 | 0 | { |
472 | 0 | // see if we are a vertical or horizontal box. |
473 | 0 | if (!GetContent() || !GetContent()->IsElement()) |
474 | 0 | return false; |
475 | 0 | |
476 | 0 | if (GetContent()->AsElement()->AttrValueIs(kNameSpaceID_None, |
477 | 0 | nsGkAtoms::equalsize, |
478 | 0 | nsGkAtoms::always, eCaseMatters)) { |
479 | 0 | aEqualSize = true; |
480 | 0 | return true; |
481 | 0 | } |
482 | 0 | |
483 | 0 | return false; |
484 | 0 | } |
485 | | |
486 | | /* Returns true if it was set. |
487 | | */ |
488 | | bool |
489 | | nsBoxFrame::GetInitialAutoStretch(bool& aStretch) |
490 | 0 | { |
491 | 0 | if (!GetContent()) |
492 | 0 | return false; |
493 | 0 | |
494 | 0 | // Check the align attribute. |
495 | 0 | if (GetContent()->IsElement()) { |
496 | 0 | static Element::AttrValuesArray strings[] = |
497 | 0 | {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr}; |
498 | 0 | int32_t index = |
499 | 0 | GetContent()->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align, |
500 | 0 | strings, eCaseMatters); |
501 | 0 | if (index != Element::ATTR_MISSING && index != 0) { |
502 | 0 | aStretch = index == 1; |
503 | 0 | return true; |
504 | 0 | } |
505 | 0 | } |
506 | 0 | |
507 | 0 | // Check the CSS box-align property. |
508 | 0 | const nsStyleXUL* boxInfo = StyleXUL(); |
509 | 0 | aStretch = (boxInfo->mBoxAlign == StyleBoxAlign::Stretch); |
510 | 0 |
|
511 | 0 | return true; |
512 | 0 | } |
513 | | |
514 | | void |
515 | | nsBoxFrame::DidReflow(nsPresContext* aPresContext, |
516 | | const ReflowInput* aReflowInput) |
517 | 0 | { |
518 | 0 | nsFrameState preserveBits = |
519 | 0 | mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN); |
520 | 0 | nsFrame::DidReflow(aPresContext, aReflowInput); |
521 | 0 | AddStateBits(preserveBits); |
522 | 0 | } |
523 | | |
524 | | bool |
525 | | nsBoxFrame::HonorPrintBackgroundSettings() |
526 | 0 | { |
527 | 0 | return !mContent->IsInNativeAnonymousSubtree() && |
528 | 0 | nsContainerFrame::HonorPrintBackgroundSettings(); |
529 | 0 | } |
530 | | |
531 | | #ifdef DO_NOISY_REFLOW |
532 | | static int myCounter = 0; |
533 | | static void printSize(char * aDesc, nscoord aSize) |
534 | | { |
535 | | printf(" %s: ", aDesc); |
536 | | if (aSize == NS_UNCONSTRAINEDSIZE) { |
537 | | printf("UC"); |
538 | | } else { |
539 | | printf("%d", aSize); |
540 | | } |
541 | | } |
542 | | #endif |
543 | | |
544 | | /* virtual */ nscoord |
545 | | nsBoxFrame::GetMinISize(gfxContext *aRenderingContext) |
546 | 0 | { |
547 | 0 | nscoord result; |
548 | 0 | DISPLAY_MIN_INLINE_SIZE(this, result); |
549 | 0 |
|
550 | 0 | nsBoxLayoutState state(PresContext(), aRenderingContext); |
551 | 0 | nsSize minSize = GetXULMinSize(state); |
552 | 0 |
|
553 | 0 | // GetXULMinSize returns border-box width, and we want to return content |
554 | 0 | // width. Since Reflow uses the reflow state's border and padding, we |
555 | 0 | // actually just want to subtract what GetXULMinSize added, which is the |
556 | 0 | // result of GetXULBorderAndPadding. |
557 | 0 | nsMargin bp; |
558 | 0 | GetXULBorderAndPadding(bp); |
559 | 0 |
|
560 | 0 | result = minSize.width - bp.LeftRight(); |
561 | 0 | result = std::max(result, 0); |
562 | 0 |
|
563 | 0 | return result; |
564 | 0 | } |
565 | | |
566 | | /* virtual */ nscoord |
567 | | nsBoxFrame::GetPrefISize(gfxContext *aRenderingContext) |
568 | 0 | { |
569 | 0 | nscoord result; |
570 | 0 | DISPLAY_PREF_INLINE_SIZE(this, result); |
571 | 0 |
|
572 | 0 | nsBoxLayoutState state(PresContext(), aRenderingContext); |
573 | 0 | nsSize prefSize = GetXULPrefSize(state); |
574 | 0 |
|
575 | 0 | // GetXULPrefSize returns border-box width, and we want to return content |
576 | 0 | // width. Since Reflow uses the reflow state's border and padding, we |
577 | 0 | // actually just want to subtract what GetXULPrefSize added, which is the |
578 | 0 | // result of GetXULBorderAndPadding. |
579 | 0 | nsMargin bp; |
580 | 0 | GetXULBorderAndPadding(bp); |
581 | 0 |
|
582 | 0 | result = prefSize.width - bp.LeftRight(); |
583 | 0 | result = std::max(result, 0); |
584 | 0 |
|
585 | 0 | return result; |
586 | 0 | } |
587 | | |
588 | | void |
589 | | nsBoxFrame::Reflow(nsPresContext* aPresContext, |
590 | | ReflowOutput& aDesiredSize, |
591 | | const ReflowInput& aReflowInput, |
592 | | nsReflowStatus& aStatus) |
593 | 0 | { |
594 | 0 | MarkInReflow(); |
595 | 0 | // If you make changes to this method, please keep nsLeafBoxFrame::Reflow |
596 | 0 | // in sync, if the changes are applicable there. |
597 | 0 |
|
598 | 0 | DO_GLOBAL_REFLOW_COUNT("nsBoxFrame"); |
599 | 0 | DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus); |
600 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
601 | 0 |
|
602 | 0 | NS_ASSERTION(aReflowInput.ComputedWidth() >=0 && |
603 | 0 | aReflowInput.ComputedHeight() >= 0, "Computed Size < 0"); |
604 | 0 |
|
605 | | #ifdef DO_NOISY_REFLOW |
606 | | printf("\n-------------Starting BoxFrame Reflow ----------------------------\n"); |
607 | | printf("%p ** nsBF::Reflow %d ", this, myCounter++); |
608 | | |
609 | | printSize("AW", aReflowInput.AvailableWidth()); |
610 | | printSize("AH", aReflowInput.AvailableHeight()); |
611 | | printSize("CW", aReflowInput.ComputedWidth()); |
612 | | printSize("CH", aReflowInput.ComputedHeight()); |
613 | | |
614 | | printf(" *\n"); |
615 | | |
616 | | #endif |
617 | |
|
618 | 0 | // create the layout state |
619 | 0 | nsBoxLayoutState state(aPresContext, aReflowInput.mRenderingContext, |
620 | 0 | &aReflowInput, aReflowInput.mReflowDepth); |
621 | 0 |
|
622 | 0 | WritingMode wm = aReflowInput.GetWritingMode(); |
623 | 0 | LogicalSize computedSize(wm, aReflowInput.ComputedISize(), |
624 | 0 | aReflowInput.ComputedBSize()); |
625 | 0 |
|
626 | 0 | LogicalMargin m = aReflowInput.ComputedLogicalBorderPadding(); |
627 | 0 | // GetXULBorderAndPadding(m); |
628 | 0 |
|
629 | 0 | LogicalSize prefSize(wm); |
630 | 0 |
|
631 | 0 | // if we are told to layout intrinsic then get our preferred size. |
632 | 0 | NS_ASSERTION(computedSize.ISize(wm) != NS_INTRINSICSIZE, |
633 | 0 | "computed inline size should always be computed"); |
634 | 0 | if (computedSize.BSize(wm) == NS_INTRINSICSIZE) { |
635 | 0 | nsSize physicalPrefSize = GetXULPrefSize(state); |
636 | 0 | nsSize minSize = GetXULMinSize(state); |
637 | 0 | nsSize maxSize = GetXULMaxSize(state); |
638 | 0 | // XXXbz isn't GetXULPrefSize supposed to bounds-check for us? |
639 | 0 | physicalPrefSize = BoundsCheck(minSize, physicalPrefSize, maxSize); |
640 | 0 | prefSize = LogicalSize(wm, physicalPrefSize); |
641 | 0 | } |
642 | 0 |
|
643 | 0 | // get our desiredSize |
644 | 0 | computedSize.ISize(wm) += m.IStart(wm) + m.IEnd(wm); |
645 | 0 |
|
646 | 0 | if (aReflowInput.ComputedBSize() == NS_INTRINSICSIZE) { |
647 | 0 | computedSize.BSize(wm) = prefSize.BSize(wm); |
648 | 0 | // prefSize is border-box but min/max constraints are content-box. |
649 | 0 | nscoord blockDirBorderPadding = |
650 | 0 | aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm); |
651 | 0 | nscoord contentBSize = computedSize.BSize(wm) - blockDirBorderPadding; |
652 | 0 | // Note: contentHeight might be negative, but that's OK because min-height |
653 | 0 | // is never negative. |
654 | 0 | computedSize.BSize(wm) = aReflowInput.ApplyMinMaxHeight(contentBSize) + |
655 | 0 | blockDirBorderPadding; |
656 | 0 | } else { |
657 | 0 | computedSize.BSize(wm) += m.BStart(wm) + m.BEnd(wm); |
658 | 0 | } |
659 | 0 |
|
660 | 0 | nsSize physicalSize = computedSize.GetPhysicalSize(wm); |
661 | 0 | nsRect r(mRect.x, mRect.y, physicalSize.width, physicalSize.height); |
662 | 0 |
|
663 | 0 | SetXULBounds(state, r); |
664 | 0 |
|
665 | 0 | // layout our children |
666 | 0 | XULLayout(state); |
667 | 0 |
|
668 | 0 | // ok our child could have gotten bigger. So lets get its bounds |
669 | 0 |
|
670 | 0 | // get the ascent |
671 | 0 | LogicalSize boxSize = GetLogicalSize(wm); |
672 | 0 | nscoord ascent = boxSize.BSize(wm); |
673 | 0 |
|
674 | 0 | // getting the ascent could be a lot of work. Don't get it if |
675 | 0 | // we are the root. The viewport doesn't care about it. |
676 | 0 | if (!(mState & NS_STATE_IS_ROOT)) { |
677 | 0 | ascent = GetXULBoxAscent(state); |
678 | 0 | } |
679 | 0 |
|
680 | 0 | aDesiredSize.SetSize(wm, boxSize); |
681 | 0 | aDesiredSize.SetBlockStartAscent(ascent); |
682 | 0 |
|
683 | 0 | aDesiredSize.mOverflowAreas = GetOverflowAreas(); |
684 | 0 |
|
685 | | #ifdef DO_NOISY_REFLOW |
686 | | { |
687 | | printf("%p ** nsBF(done) W:%d H:%d ", this, aDesiredSize.Width(), aDesiredSize.Height()); |
688 | | |
689 | | if (maxElementSize) { |
690 | | printf("MW:%d\n", *maxElementWidth); |
691 | | } else { |
692 | | printf("MW:?\n"); |
693 | | } |
694 | | |
695 | | } |
696 | | #endif |
697 | |
|
698 | 0 | ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus); |
699 | 0 |
|
700 | 0 | NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); |
701 | 0 | } |
702 | | |
703 | | nsSize |
704 | | nsBoxFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) |
705 | 0 | { |
706 | 0 | NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), |
707 | 0 | "must have rendering context"); |
708 | 0 |
|
709 | 0 | nsSize size(0,0); |
710 | 0 | DISPLAY_PREF_SIZE(this, size); |
711 | 0 | if (!DoesNeedRecalc(mPrefSize)) { |
712 | 0 | size = mPrefSize; |
713 | 0 | return size; |
714 | 0 | } |
715 | 0 | |
716 | 0 | if (IsXULCollapsed()) |
717 | 0 | return size; |
718 | 0 | |
719 | 0 | // if the size was not completely redefined in CSS then ask our children |
720 | 0 | bool widthSet, heightSet; |
721 | 0 | if (!nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet)) |
722 | 0 | { |
723 | 0 | if (mLayoutManager) { |
724 | 0 | nsSize layoutSize = mLayoutManager->GetXULPrefSize(this, aBoxLayoutState); |
725 | 0 | if (!widthSet) |
726 | 0 | size.width = layoutSize.width; |
727 | 0 | if (!heightSet) |
728 | 0 | size.height = layoutSize.height; |
729 | 0 | } |
730 | 0 | else { |
731 | 0 | size = nsBox::GetXULPrefSize(aBoxLayoutState); |
732 | 0 | } |
733 | 0 | } |
734 | 0 |
|
735 | 0 | nsSize minSize = GetXULMinSize(aBoxLayoutState); |
736 | 0 | nsSize maxSize = GetXULMaxSize(aBoxLayoutState); |
737 | 0 | mPrefSize = BoundsCheck(minSize, size, maxSize); |
738 | 0 |
|
739 | 0 | return mPrefSize; |
740 | 0 | } |
741 | | |
742 | | nscoord |
743 | | nsBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState) |
744 | 0 | { |
745 | 0 | if (!DoesNeedRecalc(mAscent)) |
746 | 0 | return mAscent; |
747 | 0 | |
748 | 0 | if (IsXULCollapsed()) |
749 | 0 | return 0; |
750 | 0 | |
751 | 0 | if (mLayoutManager) |
752 | 0 | mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState); |
753 | 0 | else |
754 | 0 | mAscent = nsBox::GetXULBoxAscent(aBoxLayoutState); |
755 | 0 |
|
756 | 0 | return mAscent; |
757 | 0 | } |
758 | | |
759 | | nsSize |
760 | | nsBoxFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) |
761 | 0 | { |
762 | 0 | NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), |
763 | 0 | "must have rendering context"); |
764 | 0 |
|
765 | 0 | nsSize size(0,0); |
766 | 0 | DISPLAY_MIN_SIZE(this, size); |
767 | 0 | if (!DoesNeedRecalc(mMinSize)) { |
768 | 0 | size = mMinSize; |
769 | 0 | return size; |
770 | 0 | } |
771 | 0 | |
772 | 0 | if (IsXULCollapsed()) |
773 | 0 | return size; |
774 | 0 | |
775 | 0 | // if the size was not completely redefined in CSS then ask our children |
776 | 0 | bool widthSet, heightSet; |
777 | 0 | if (!nsIFrame::AddXULMinSize(aBoxLayoutState, this, size, widthSet, heightSet)) |
778 | 0 | { |
779 | 0 | if (mLayoutManager) { |
780 | 0 | nsSize layoutSize = mLayoutManager->GetXULMinSize(this, aBoxLayoutState); |
781 | 0 | if (!widthSet) |
782 | 0 | size.width = layoutSize.width; |
783 | 0 | if (!heightSet) |
784 | 0 | size.height = layoutSize.height; |
785 | 0 | } |
786 | 0 | else { |
787 | 0 | size = nsBox::GetXULMinSize(aBoxLayoutState); |
788 | 0 | } |
789 | 0 | } |
790 | 0 |
|
791 | 0 | mMinSize = size; |
792 | 0 |
|
793 | 0 | return size; |
794 | 0 | } |
795 | | |
796 | | nsSize |
797 | | nsBoxFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState) |
798 | 0 | { |
799 | 0 | NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), |
800 | 0 | "must have rendering context"); |
801 | 0 |
|
802 | 0 | nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); |
803 | 0 | DISPLAY_MAX_SIZE(this, size); |
804 | 0 | if (!DoesNeedRecalc(mMaxSize)) { |
805 | 0 | size = mMaxSize; |
806 | 0 | return size; |
807 | 0 | } |
808 | 0 | |
809 | 0 | if (IsXULCollapsed()) |
810 | 0 | return size; |
811 | 0 | |
812 | 0 | // if the size was not completely redefined in CSS then ask our children |
813 | 0 | bool widthSet, heightSet; |
814 | 0 | if (!nsIFrame::AddXULMaxSize(this, size, widthSet, heightSet)) |
815 | 0 | { |
816 | 0 | if (mLayoutManager) { |
817 | 0 | nsSize layoutSize = mLayoutManager->GetXULMaxSize(this, aBoxLayoutState); |
818 | 0 | if (!widthSet) |
819 | 0 | size.width = layoutSize.width; |
820 | 0 | if (!heightSet) |
821 | 0 | size.height = layoutSize.height; |
822 | 0 | } |
823 | 0 | else { |
824 | 0 | size = nsBox::GetXULMaxSize(aBoxLayoutState); |
825 | 0 | } |
826 | 0 | } |
827 | 0 |
|
828 | 0 | mMaxSize = size; |
829 | 0 |
|
830 | 0 | return size; |
831 | 0 | } |
832 | | |
833 | | nscoord |
834 | | nsBoxFrame::GetXULFlex() |
835 | 0 | { |
836 | 0 | if (!DoesNeedRecalc(mFlex)) |
837 | 0 | return mFlex; |
838 | 0 | |
839 | 0 | mFlex = nsBox::GetXULFlex(); |
840 | 0 |
|
841 | 0 | return mFlex; |
842 | 0 | } |
843 | | |
844 | | /** |
845 | | * If subclassing please subclass this method not layout. |
846 | | * layout will call this method. |
847 | | */ |
848 | | NS_IMETHODIMP |
849 | | nsBoxFrame::DoXULLayout(nsBoxLayoutState& aState) |
850 | 0 | { |
851 | 0 | uint32_t oldFlags = aState.LayoutFlags(); |
852 | 0 | aState.SetLayoutFlags(0); |
853 | 0 |
|
854 | 0 | nsresult rv = NS_OK; |
855 | 0 | if (mLayoutManager) { |
856 | 0 | CoordNeedsRecalc(mAscent); |
857 | 0 | rv = mLayoutManager->XULLayout(this, aState); |
858 | 0 | } |
859 | 0 |
|
860 | 0 | aState.SetLayoutFlags(oldFlags); |
861 | 0 |
|
862 | 0 | if (HasAbsolutelyPositionedChildren()) { |
863 | 0 | // Set up a |reflowInput| to pass into ReflowAbsoluteFrames |
864 | 0 | WritingMode wm = GetWritingMode(); |
865 | 0 | ReflowInput reflowInput(aState.PresContext(), this, |
866 | 0 | aState.GetRenderingContext(), |
867 | 0 | LogicalSize(wm, GetLogicalSize().ISize(wm), |
868 | 0 | NS_UNCONSTRAINEDSIZE)); |
869 | 0 |
|
870 | 0 | // Set up a |desiredSize| to pass into ReflowAbsoluteFrames |
871 | 0 | ReflowOutput desiredSize(reflowInput); |
872 | 0 | desiredSize.Width() = mRect.width; |
873 | 0 | desiredSize.Height() = mRect.height; |
874 | 0 |
|
875 | 0 | // get the ascent (cribbed from ::Reflow) |
876 | 0 | nscoord ascent = mRect.height; |
877 | 0 |
|
878 | 0 | // getting the ascent could be a lot of work. Don't get it if |
879 | 0 | // we are the root. The viewport doesn't care about it. |
880 | 0 | if (!(mState & NS_STATE_IS_ROOT)) { |
881 | 0 | ascent = GetXULBoxAscent(aState); |
882 | 0 | } |
883 | 0 | desiredSize.SetBlockStartAscent(ascent); |
884 | 0 | desiredSize.mOverflowAreas = GetOverflowAreas(); |
885 | 0 |
|
886 | 0 | AddStateBits(NS_FRAME_IN_REFLOW); |
887 | 0 | // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames |
888 | 0 | // (just a dummy value; hopefully that's OK) |
889 | 0 | nsReflowStatus reflowStatus; |
890 | 0 | ReflowAbsoluteFrames(aState.PresContext(), desiredSize, |
891 | 0 | reflowInput, reflowStatus); |
892 | 0 | RemoveStateBits(NS_FRAME_IN_REFLOW); |
893 | 0 | } |
894 | 0 |
|
895 | 0 | return rv; |
896 | 0 | } |
897 | | |
898 | | void |
899 | | nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
900 | 0 | { |
901 | 0 | // unregister access key |
902 | 0 | RegUnregAccessKey(false); |
903 | 0 |
|
904 | 0 | // clean up the container box's layout manager and child boxes |
905 | 0 | SetXULLayoutManager(nullptr); |
906 | 0 |
|
907 | 0 | nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
908 | 0 | } |
909 | | |
910 | | /* virtual */ void |
911 | | nsBoxFrame::MarkIntrinsicISizesDirty() |
912 | 0 | { |
913 | 0 | SizeNeedsRecalc(mPrefSize); |
914 | 0 | SizeNeedsRecalc(mMinSize); |
915 | 0 | SizeNeedsRecalc(mMaxSize); |
916 | 0 | CoordNeedsRecalc(mFlex); |
917 | 0 | CoordNeedsRecalc(mAscent); |
918 | 0 |
|
919 | 0 | if (mLayoutManager) { |
920 | 0 | nsBoxLayoutState state(PresContext()); |
921 | 0 | mLayoutManager->IntrinsicISizesDirty(this, state); |
922 | 0 | } |
923 | 0 |
|
924 | 0 | // Don't call base class method, since everything it does is within an |
925 | 0 | // IsXULBoxWrapped check. |
926 | 0 | } |
927 | | |
928 | | void |
929 | | nsBoxFrame::RemoveFrame(ChildListID aListID, |
930 | | nsIFrame* aOldFrame) |
931 | 0 | { |
932 | 0 | MOZ_ASSERT(aListID == kPrincipalList, "We don't support out-of-flow kids"); |
933 | 0 |
|
934 | 0 | nsPresContext* presContext = PresContext(); |
935 | 0 | nsBoxLayoutState state(presContext); |
936 | 0 |
|
937 | 0 | // remove the child frame |
938 | 0 | mFrames.RemoveFrame(aOldFrame); |
939 | 0 |
|
940 | 0 | // notify the layout manager |
941 | 0 | if (mLayoutManager) |
942 | 0 | mLayoutManager->ChildrenRemoved(this, state, aOldFrame); |
943 | 0 |
|
944 | 0 | // destroy the child frame |
945 | 0 | aOldFrame->Destroy(); |
946 | 0 |
|
947 | 0 | // mark us dirty and generate a reflow command |
948 | 0 | PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
949 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
950 | 0 | } |
951 | | |
952 | | void |
953 | | nsBoxFrame::InsertFrames(ChildListID aListID, |
954 | | nsIFrame* aPrevFrame, |
955 | | nsFrameList& aFrameList) |
956 | 0 | { |
957 | 0 | NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
958 | 0 | "inserting after sibling frame with different parent"); |
959 | 0 | NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame), |
960 | 0 | "inserting after sibling frame not in our child list"); |
961 | 0 | MOZ_ASSERT(aListID == kPrincipalList, "We don't support out-of-flow kids"); |
962 | 0 |
|
963 | 0 | nsBoxLayoutState state(PresContext()); |
964 | 0 |
|
965 | 0 | // insert the child frames |
966 | 0 | const nsFrameList::Slice& newFrames = |
967 | 0 | mFrames.InsertFrames(this, aPrevFrame, aFrameList); |
968 | 0 |
|
969 | 0 | // notify the layout manager |
970 | 0 | if (mLayoutManager) |
971 | 0 | mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames); |
972 | 0 |
|
973 | 0 | // Make sure to check box order _after_ notifying the layout |
974 | 0 | // manager; otherwise the slice we give the layout manager will |
975 | 0 | // just be bogus. If the layout manager cares about the order, we |
976 | 0 | // just lose. |
977 | 0 | CheckBoxOrder(); |
978 | 0 |
|
979 | 0 | PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
980 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
981 | 0 | } |
982 | | |
983 | | |
984 | | void |
985 | | nsBoxFrame::AppendFrames(ChildListID aListID, |
986 | | nsFrameList& aFrameList) |
987 | 0 | { |
988 | 0 | MOZ_ASSERT(aListID == kPrincipalList, "We don't support out-of-flow kids"); |
989 | 0 |
|
990 | 0 | nsBoxLayoutState state(PresContext()); |
991 | 0 |
|
992 | 0 | // append the new frames |
993 | 0 | const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList); |
994 | 0 |
|
995 | 0 | // notify the layout manager |
996 | 0 | if (mLayoutManager) |
997 | 0 | mLayoutManager->ChildrenAppended(this, state, newFrames); |
998 | 0 |
|
999 | 0 | // Make sure to check box order _after_ notifying the layout |
1000 | 0 | // manager; otherwise the slice we give the layout manager will |
1001 | 0 | // just be bogus. If the layout manager cares about the order, we |
1002 | 0 | // just lose. |
1003 | 0 | CheckBoxOrder(); |
1004 | 0 |
|
1005 | 0 | // XXXbz why is this NS_FRAME_FIRST_REFLOW check here? |
1006 | 0 | if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
1007 | 0 | PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
1008 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
1009 | 0 | } |
1010 | 0 | } |
1011 | | |
1012 | | /* virtual */ nsContainerFrame* |
1013 | | nsBoxFrame::GetContentInsertionFrame() |
1014 | 0 | { |
1015 | 0 | if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) |
1016 | 0 | return PrincipalChildList().FirstChild()->GetContentInsertionFrame(); |
1017 | 0 | return nsContainerFrame::GetContentInsertionFrame(); |
1018 | 0 | } |
1019 | | |
1020 | | nsresult |
1021 | | nsBoxFrame::AttributeChanged(int32_t aNameSpaceID, |
1022 | | nsAtom* aAttribute, |
1023 | | int32_t aModType) |
1024 | 0 | { |
1025 | 0 | nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, |
1026 | 0 | aModType); |
1027 | 0 |
|
1028 | 0 | // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a |
1029 | 0 | // <window>. |
1030 | 0 | if (mContent->IsAnyOfXULElements(nsGkAtoms::window, |
1031 | 0 | nsGkAtoms::page, |
1032 | 0 | nsGkAtoms::dialog, |
1033 | 0 | nsGkAtoms::wizard) && |
1034 | 0 | (nsGkAtoms::width == aAttribute || |
1035 | 0 | nsGkAtoms::height == aAttribute || |
1036 | 0 | nsGkAtoms::screenX == aAttribute || |
1037 | 0 | nsGkAtoms::screenY == aAttribute || |
1038 | 0 | nsGkAtoms::sizemode == aAttribute)) { |
1039 | 0 | return rv; |
1040 | 0 | } |
1041 | 0 | |
1042 | 0 | if (aAttribute == nsGkAtoms::width || |
1043 | 0 | aAttribute == nsGkAtoms::height || |
1044 | 0 | aAttribute == nsGkAtoms::align || |
1045 | 0 | aAttribute == nsGkAtoms::valign || |
1046 | 0 | aAttribute == nsGkAtoms::left || |
1047 | 0 | aAttribute == nsGkAtoms::top || |
1048 | 0 | aAttribute == nsGkAtoms::right || |
1049 | 0 | aAttribute == nsGkAtoms::bottom || |
1050 | 0 | aAttribute == nsGkAtoms::start || |
1051 | 0 | aAttribute == nsGkAtoms::end || |
1052 | 0 | aAttribute == nsGkAtoms::minwidth || |
1053 | 0 | aAttribute == nsGkAtoms::maxwidth || |
1054 | 0 | aAttribute == nsGkAtoms::minheight || |
1055 | 0 | aAttribute == nsGkAtoms::maxheight || |
1056 | 0 | aAttribute == nsGkAtoms::flex || |
1057 | 0 | aAttribute == nsGkAtoms::orient || |
1058 | 0 | aAttribute == nsGkAtoms::pack || |
1059 | 0 | aAttribute == nsGkAtoms::dir || |
1060 | 0 | aAttribute == nsGkAtoms::mousethrough || |
1061 | 0 | aAttribute == nsGkAtoms::equalsize) { |
1062 | 0 |
|
1063 | 0 | if (aAttribute == nsGkAtoms::align || |
1064 | 0 | aAttribute == nsGkAtoms::valign || |
1065 | 0 | aAttribute == nsGkAtoms::orient || |
1066 | 0 | aAttribute == nsGkAtoms::pack || |
1067 | 0 | aAttribute == nsGkAtoms::dir) { |
1068 | 0 |
|
1069 | 0 | mValign = nsBoxFrame::vAlign_Top; |
1070 | 0 | mHalign = nsBoxFrame::hAlign_Left; |
1071 | 0 |
|
1072 | 0 | bool orient = true; |
1073 | 0 | GetInitialOrientation(orient); |
1074 | 0 | if (orient) |
1075 | 0 | AddStateBits(NS_STATE_IS_HORIZONTAL); |
1076 | 0 | else |
1077 | 0 | RemoveStateBits(NS_STATE_IS_HORIZONTAL); |
1078 | 0 |
|
1079 | 0 | bool normal = true; |
1080 | 0 | GetInitialDirection(normal); |
1081 | 0 | if (normal) |
1082 | 0 | AddStateBits(NS_STATE_IS_DIRECTION_NORMAL); |
1083 | 0 | else |
1084 | 0 | RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL); |
1085 | 0 |
|
1086 | 0 | GetInitialVAlignment(mValign); |
1087 | 0 | GetInitialHAlignment(mHalign); |
1088 | 0 |
|
1089 | 0 | bool equalSize = false; |
1090 | 0 | GetInitialEqualSize(equalSize); |
1091 | 0 | if (equalSize) |
1092 | 0 | AddStateBits(NS_STATE_EQUAL_SIZE); |
1093 | 0 | else |
1094 | 0 | RemoveStateBits(NS_STATE_EQUAL_SIZE); |
1095 | 0 |
|
1096 | 0 | bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH); |
1097 | 0 | GetInitialAutoStretch(autostretch); |
1098 | 0 | if (autostretch) |
1099 | 0 | AddStateBits(NS_STATE_AUTO_STRETCH); |
1100 | 0 | else |
1101 | 0 | RemoveStateBits(NS_STATE_AUTO_STRETCH); |
1102 | 0 | } |
1103 | 0 | else if (aAttribute == nsGkAtoms::left || |
1104 | 0 | aAttribute == nsGkAtoms::top || |
1105 | 0 | aAttribute == nsGkAtoms::right || |
1106 | 0 | aAttribute == nsGkAtoms::bottom || |
1107 | 0 | aAttribute == nsGkAtoms::start || |
1108 | 0 | aAttribute == nsGkAtoms::end) { |
1109 | 0 | RemoveStateBits(NS_STATE_STACK_NOT_POSITIONED); |
1110 | 0 | } |
1111 | 0 | else if (aAttribute == nsGkAtoms::mousethrough) { |
1112 | 0 | UpdateMouseThrough(); |
1113 | 0 | } |
1114 | 0 |
|
1115 | 0 | PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange, |
1116 | 0 | NS_FRAME_IS_DIRTY); |
1117 | 0 | } |
1118 | 0 | else if (aAttribute == nsGkAtoms::ordinal) { |
1119 | 0 | nsIFrame* parent = GetParentXULBox(this); |
1120 | 0 | // If our parent is not a box, there's not much we can do... but in that |
1121 | 0 | // case our ordinal doesn't matter anyway, so that's ok. |
1122 | 0 | // Also don't bother with popup frames since they are kept on the |
1123 | 0 | // kPopupList and XULRelayoutChildAtOrdinal() only handles |
1124 | 0 | // principal children. |
1125 | 0 | if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) && |
1126 | 0 | StyleDisplay()->mDisplay != mozilla::StyleDisplay::MozPopup) { |
1127 | 0 | parent->XULRelayoutChildAtOrdinal(this); |
1128 | 0 | // XXXldb Should this instead be a tree change on the child or parent? |
1129 | 0 | PresShell()->FrameNeedsReflow(parent, nsIPresShell::eStyleChange, |
1130 | 0 | NS_FRAME_IS_DIRTY); |
1131 | 0 | } |
1132 | 0 | } |
1133 | 0 | // If the accesskey changed, register for the new value |
1134 | 0 | // The old value has been unregistered in nsXULElement::SetAttr |
1135 | 0 | else if (aAttribute == nsGkAtoms::accesskey) { |
1136 | 0 | RegUnregAccessKey(true); |
1137 | 0 | } |
1138 | 0 | else if (aAttribute == nsGkAtoms::rows && |
1139 | 0 | mContent->IsXULElement(nsGkAtoms::tree)) { |
1140 | 0 | // Reflow ourselves and all our children if "rows" changes, since |
1141 | 0 | // nsTreeBodyFrame's layout reads this from its parent (this frame). |
1142 | 0 | PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange, |
1143 | 0 | NS_FRAME_IS_DIRTY); |
1144 | 0 | } |
1145 | 0 |
|
1146 | 0 | return rv; |
1147 | 0 | } |
1148 | | |
1149 | | void |
1150 | | nsBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
1151 | | const nsDisplayListSet& aLists) |
1152 | 0 | { |
1153 | 0 | bool forceLayer = false; |
1154 | 0 |
|
1155 | 0 | if (GetContent()->IsXULElement()) { |
1156 | 0 | // forcelayer is only supported on XUL elements with box layout |
1157 | 0 | if (GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) { |
1158 | 0 | forceLayer = true; |
1159 | 0 | } |
1160 | 0 | // Check for frames that are marked as a part of the region used |
1161 | 0 | // in calculating glass margins on Windows. |
1162 | 0 | const nsStyleDisplay* styles = StyleDisplay(); |
1163 | 0 | if (styles && styles->mAppearance == StyleAppearance::MozWinExcludeGlass) { |
1164 | 0 | aBuilder->AddWindowExcludeGlassRegion( |
1165 | 0 | this, |
1166 | 0 | nsRect(aBuilder->ToReferenceFrame(this), GetSize())); |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 |
|
1170 | 0 | nsDisplayListCollection tempLists(aBuilder); |
1171 | 0 | const nsDisplayListSet& destination = forceLayer ? tempLists : aLists; |
1172 | 0 |
|
1173 | 0 | DisplayBorderBackgroundOutline(aBuilder, destination); |
1174 | 0 |
|
1175 | 0 | Maybe<nsDisplayListBuilder::AutoContainerASRTracker> contASRTracker; |
1176 | 0 | if (forceLayer) { |
1177 | 0 | contASRTracker.emplace(aBuilder); |
1178 | 0 | } |
1179 | 0 |
|
1180 | 0 | BuildDisplayListForChildren(aBuilder, destination); |
1181 | 0 |
|
1182 | 0 | // see if we have to draw a selection frame around this container |
1183 | 0 | DisplaySelectionOverlay(aBuilder, destination.Content()); |
1184 | 0 |
|
1185 | 0 | if (forceLayer) { |
1186 | 0 | // This is a bit of a hack. Collect up all descendant display items |
1187 | 0 | // and merge them into a single Content() list. This can cause us |
1188 | 0 | // to violate CSS stacking order, but forceLayer is a magic |
1189 | 0 | // XUL-only extension anyway. |
1190 | 0 | nsDisplayList masterList; |
1191 | 0 | masterList.AppendToTop(tempLists.BorderBackground()); |
1192 | 0 | masterList.AppendToTop(tempLists.BlockBorderBackgrounds()); |
1193 | 0 | masterList.AppendToTop(tempLists.Floats()); |
1194 | 0 | masterList.AppendToTop(tempLists.Content()); |
1195 | 0 | masterList.AppendToTop(tempLists.PositionedDescendants()); |
1196 | 0 | masterList.AppendToTop(tempLists.Outlines()); |
1197 | 0 |
|
1198 | 0 | const ActiveScrolledRoot* ownLayerASR = contASRTracker->GetContainerASR(); |
1199 | 0 |
|
1200 | 0 | DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder); |
1201 | 0 |
|
1202 | 0 | // Wrap the list to make it its own layer |
1203 | 0 | aLists.Content()->AppendToTop( |
1204 | 0 | MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &masterList, ownLayerASR, |
1205 | 0 | nsDisplayOwnLayerFlags::eNone, |
1206 | 0 | mozilla::layers::ScrollbarData{}, true, true)); |
1207 | 0 | } |
1208 | 0 | } |
1209 | | |
1210 | | void |
1211 | | nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, |
1212 | | const nsDisplayListSet& aLists) |
1213 | 0 | { |
1214 | 0 | nsIFrame* kid = mFrames.FirstChild(); |
1215 | 0 | // Put each child's background onto the BlockBorderBackgrounds list |
1216 | 0 | // to emulate the existing two-layer XUL painting scheme. |
1217 | 0 | nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds()); |
1218 | 0 | // The children should be in the right order |
1219 | 0 | while (kid) { |
1220 | 0 | BuildDisplayListForChild(aBuilder, kid, set); |
1221 | 0 | kid = kid->GetNextSibling(); |
1222 | 0 | } |
1223 | 0 | } |
1224 | | |
1225 | | #ifdef DEBUG_FRAME_DUMP |
1226 | | nsresult |
1227 | | nsBoxFrame::GetFrameName(nsAString& aResult) const |
1228 | | { |
1229 | | return MakeFrameName(NS_LITERAL_STRING("Box"), aResult); |
1230 | | } |
1231 | | #endif |
1232 | | |
1233 | | // If you make changes to this function, check its counterparts |
1234 | | // in nsTextBoxFrame and nsXULLabelFrame |
1235 | | void |
1236 | | nsBoxFrame::RegUnregAccessKey(bool aDoReg) |
1237 | 0 | { |
1238 | 0 | MOZ_ASSERT(mContent); |
1239 | 0 |
|
1240 | 0 | // only support accesskeys for the following elements |
1241 | 0 | if (!mContent->IsAnyOfXULElements(nsGkAtoms::button, |
1242 | 0 | nsGkAtoms::toolbarbutton, |
1243 | 0 | nsGkAtoms::checkbox, |
1244 | 0 | nsGkAtoms::textbox, |
1245 | 0 | nsGkAtoms::tab, |
1246 | 0 | nsGkAtoms::radio)) { |
1247 | 0 | return; |
1248 | 0 | } |
1249 | 0 | |
1250 | 0 | nsAutoString accessKey; |
1251 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey); |
1252 | 0 |
|
1253 | 0 | if (accessKey.IsEmpty()) |
1254 | 0 | return; |
1255 | 0 | |
1256 | 0 | // With a valid PresContext we can get the ESM |
1257 | 0 | // and register the access key |
1258 | 0 | EventStateManager* esm = PresContext()->EventStateManager(); |
1259 | 0 |
|
1260 | 0 | uint32_t key = accessKey.First(); |
1261 | 0 | if (aDoReg) |
1262 | 0 | esm->RegisterAccessKey(mContent->AsElement(), key); |
1263 | 0 | else |
1264 | 0 | esm->UnregisterAccessKey(mContent->AsElement(), key); |
1265 | 0 | } |
1266 | | |
1267 | | void |
1268 | | nsBoxFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) |
1269 | 0 | { |
1270 | 0 | if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) { |
1271 | 0 | aResult.AppendElement(OwnedAnonBox(PrincipalChildList().FirstChild())); |
1272 | 0 | } |
1273 | 0 | } |
1274 | | |
1275 | | // Helper less-than-or-equal function, used in CheckBoxOrder() as a |
1276 | | // template-parameter for the sorting functions. |
1277 | | static bool |
1278 | | IsBoxOrdinalLEQ(nsIFrame* aFrame1, |
1279 | | nsIFrame* aFrame2) |
1280 | 0 | { |
1281 | 0 | // If we've got a placeholder frame, use its out-of-flow frame's ordinal val. |
1282 | 0 | nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1); |
1283 | 0 | nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2); |
1284 | 0 | return aRealFrame1->GetXULOrdinal() <= aRealFrame2->GetXULOrdinal(); |
1285 | 0 | } |
1286 | | |
1287 | | void |
1288 | | nsBoxFrame::CheckBoxOrder() |
1289 | 0 | { |
1290 | 0 | if (!nsIFrame::IsFrameListSorted<IsBoxOrdinalLEQ>(mFrames)) { |
1291 | 0 | nsIFrame::SortFrameList<IsBoxOrdinalLEQ>(mFrames); |
1292 | 0 | } |
1293 | 0 | } |
1294 | | |
1295 | | nsresult |
1296 | | nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect) |
1297 | 0 | { |
1298 | 0 | // get the current rect |
1299 | 0 | nsRect oldRect(aBox->GetRect()); |
1300 | 0 | aBox->SetXULBounds(aState, aRect); |
1301 | 0 |
|
1302 | 0 | bool layout = NS_SUBTREE_DIRTY(aBox); |
1303 | 0 |
|
1304 | 0 | if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height)) { |
1305 | 0 | return aBox->XULLayout(aState); |
1306 | 0 | } |
1307 | 0 | |
1308 | 0 | return NS_OK; |
1309 | 0 | } |
1310 | | |
1311 | | nsresult |
1312 | | nsBoxFrame::XULRelayoutChildAtOrdinal(nsIFrame* aChild) |
1313 | 0 | { |
1314 | 0 | uint32_t ord = aChild->GetXULOrdinal(); |
1315 | 0 |
|
1316 | 0 | nsIFrame* child = mFrames.FirstChild(); |
1317 | 0 | nsIFrame* newPrevSib = nullptr; |
1318 | 0 |
|
1319 | 0 | while (child) { |
1320 | 0 | if (ord < child->GetXULOrdinal()) { |
1321 | 0 | break; |
1322 | 0 | } |
1323 | 0 | |
1324 | 0 | if (child != aChild) { |
1325 | 0 | newPrevSib = child; |
1326 | 0 | } |
1327 | 0 |
|
1328 | 0 | child = GetNextXULBox(child); |
1329 | 0 | } |
1330 | 0 |
|
1331 | 0 | if (aChild->GetPrevSibling() == newPrevSib) { |
1332 | 0 | // This box is not moving. |
1333 | 0 | return NS_OK; |
1334 | 0 | } |
1335 | 0 | |
1336 | 0 | // Take |aChild| out of its old position in the child list. |
1337 | 0 | mFrames.RemoveFrame(aChild); |
1338 | 0 |
|
1339 | 0 | // Insert it after |newPrevSib| or at the start if it's null. |
1340 | 0 | mFrames.InsertFrame(nullptr, newPrevSib, aChild); |
1341 | 0 |
|
1342 | 0 | return NS_OK; |
1343 | 0 | } |
1344 | | |
1345 | | /** |
1346 | | * This wrapper class lets us redirect mouse hits from descendant frames |
1347 | | * of a menu to the menu itself, if they didn't specify 'allowevents'. |
1348 | | * |
1349 | | * The wrapper simply turns a hit on a descendant element |
1350 | | * into a hit on the menu itself, unless there is an element between the target |
1351 | | * and the menu with the "allowevents" attribute. |
1352 | | * |
1353 | | * This is used by nsMenuFrame and nsTreeColFrame. |
1354 | | * |
1355 | | * Note that turning a hit on a descendant element into nullptr, so events |
1356 | | * could fall through to the menu background, might be an appealing simplification |
1357 | | * but it would mean slightly strange behaviour in some cases, because grabber |
1358 | | * wrappers can be created for many individual lists and items, so the exact |
1359 | | * fallthrough behaviour would be complex. E.g. an element with "allowevents" |
1360 | | * on top of the Content() list could receive the event even if it was covered |
1361 | | * by a PositionedDescenants() element without "allowevents". It is best to |
1362 | | * never convert a non-null hit into null. |
1363 | | */ |
1364 | | // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do. |
1365 | | // I've made 'allowevents' affect child elements because that seems the only |
1366 | | // reasonable thing to do. |
1367 | | class nsDisplayXULEventRedirector final : public nsDisplayWrapList |
1368 | | { |
1369 | | public: |
1370 | | nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder, |
1371 | | nsIFrame* aFrame, nsDisplayItem* aItem, |
1372 | | nsIFrame* aTargetFrame) |
1373 | 0 | : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {} |
1374 | | nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder, |
1375 | | nsIFrame* aFrame, nsDisplayList* aList, |
1376 | | nsIFrame* aTargetFrame) |
1377 | 0 | : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {} |
1378 | | virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
1379 | | HitTestState* aState, |
1380 | | nsTArray<nsIFrame*> *aOutFrames) override; |
1381 | 0 | virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { |
1382 | 0 | return false; |
1383 | 0 | } |
1384 | | NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR) |
1385 | | private: |
1386 | | nsIFrame* mTargetFrame; |
1387 | | }; |
1388 | | |
1389 | | void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder, |
1390 | | const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) |
1391 | 0 | { |
1392 | 0 | nsTArray<nsIFrame*> outFrames; |
1393 | 0 | mList.HitTest(aBuilder, aRect, aState, &outFrames); |
1394 | 0 |
|
1395 | 0 | bool topMostAdded = false; |
1396 | 0 | uint32_t localLength = outFrames.Length(); |
1397 | 0 |
|
1398 | 0 | for (uint32_t i = 0; i < localLength; i++) { |
1399 | 0 |
|
1400 | 0 | for (nsIContent* content = outFrames.ElementAt(i)->GetContent(); |
1401 | 0 | content && content != mTargetFrame->GetContent(); |
1402 | 0 | content = content->GetParent()) { |
1403 | 0 | if (!content->IsElement() || |
1404 | 0 | !content->AsElement()->AttrValueIs(kNameSpaceID_None, |
1405 | 0 | nsGkAtoms::allowevents, |
1406 | 0 | nsGkAtoms::_true, eCaseMatters)) { |
1407 | 0 | continue; |
1408 | 0 | } |
1409 | 0 | |
1410 | 0 | // Events are allowed on 'frame', so let it go. |
1411 | 0 | aOutFrames->AppendElement(outFrames.ElementAt(i)); |
1412 | 0 | topMostAdded = true; |
1413 | 0 | } |
1414 | 0 |
|
1415 | 0 | // If there was no hit on the topmost frame or its ancestors, |
1416 | 0 | // add the target frame itself as the first candidate (see bug 562554). |
1417 | 0 | if (!topMostAdded) { |
1418 | 0 | topMostAdded = true; |
1419 | 0 | aOutFrames->AppendElement(mTargetFrame); |
1420 | 0 | } |
1421 | 0 | } |
1422 | 0 | } |
1423 | | |
1424 | | class nsXULEventRedirectorWrapper final : public nsDisplayWrapper |
1425 | | { |
1426 | | public: |
1427 | | explicit nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame) |
1428 | 0 | : mTargetFrame(aTargetFrame) {} |
1429 | | virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder, |
1430 | | nsIFrame* aFrame, |
1431 | 0 | nsDisplayList* aList) override { |
1432 | 0 | return MakeDisplayItem<nsDisplayXULEventRedirector>(aBuilder, aFrame, aList, |
1433 | 0 | mTargetFrame); |
1434 | 0 | } |
1435 | | virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder, |
1436 | 0 | nsDisplayItem* aItem) override { |
1437 | 0 | return MakeDisplayItem<nsDisplayXULEventRedirector>(aBuilder, aItem->Frame(), aItem, |
1438 | 0 | mTargetFrame); |
1439 | 0 | } |
1440 | | private: |
1441 | | nsIFrame* mTargetFrame; |
1442 | | }; |
1443 | | |
1444 | | void |
1445 | | nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder* aBuilder, |
1446 | | const nsDisplayListSet& aIn, |
1447 | | const nsDisplayListSet& aOut) |
1448 | 0 | { |
1449 | 0 | nsXULEventRedirectorWrapper wrapper(this); |
1450 | 0 | wrapper.WrapLists(aBuilder, this, aIn, aOut); |
1451 | 0 | } |
1452 | | |
1453 | | bool |
1454 | 0 | nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) { |
1455 | 0 | LayoutDeviceIntPoint refPoint; |
1456 | 0 | bool res = GetEventPoint(aEvent, refPoint); |
1457 | 0 | aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo( |
1458 | 0 | aEvent, refPoint, this); |
1459 | 0 | return res; |
1460 | 0 | } |
1461 | | |
1462 | | bool |
1463 | 0 | nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) { |
1464 | 0 | NS_ENSURE_TRUE(aEvent, false); |
1465 | 0 |
|
1466 | 0 | WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); |
1467 | 0 | if (touchEvent) { |
1468 | 0 | // return false if there is more than one touch on the page, or if |
1469 | 0 | // we can't find a touch point |
1470 | 0 | if (touchEvent->mTouches.Length() != 1) { |
1471 | 0 | return false; |
1472 | 0 | } |
1473 | 0 | |
1474 | 0 | dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0); |
1475 | 0 | if (!touch) { |
1476 | 0 | return false; |
1477 | 0 | } |
1478 | 0 | aPoint = touch->mRefPoint; |
1479 | 0 | } else { |
1480 | 0 | aPoint = aEvent->mRefPoint; |
1481 | 0 | } |
1482 | 0 | return true; |
1483 | 0 | } |