1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : //
41 : // Eric Vaughan
42 : // Netscape Communications
43 : //
44 : // See documentation in associated header file
45 : //
46 :
47 : // How boxes layout
48 : // ----------------
49 : // Boxes layout a bit differently than html. html does a bottom up layout. Where boxes do a top down.
50 : // 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes.
51 : // 2) It then adds them up to determine its size.
52 : // 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size
53 : // otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if
54 : // Necessary.
55 : //
56 : // However there is a catch. Some html components like block frames can not determine their preferred size.
57 : // this is their size if they were laid out intrinsically. So the box will flow the child to determine this can
58 : // cache the value.
59 :
60 : // Boxes and Incremental Reflow
61 : // ----------------------------
62 : // Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental
63 : // reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change
64 : // any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared
65 : // so when asked for there current size they can relayout themselves.
66 :
67 : #include "nsBoxLayoutState.h"
68 : #include "nsBoxFrame.h"
69 : #include "nsStyleContext.h"
70 : #include "nsPresContext.h"
71 : #include "nsCOMPtr.h"
72 : #include "nsINameSpaceManager.h"
73 : #include "nsGkAtoms.h"
74 : #include "nsIContent.h"
75 : #include "nsHTMLParts.h"
76 : #include "nsIViewManager.h"
77 : #include "nsIView.h"
78 : #include "nsIPresShell.h"
79 : #include "nsCSSRendering.h"
80 : #include "nsIServiceManager.h"
81 : #include "nsBoxLayout.h"
82 : #include "nsSprocketLayout.h"
83 : #include "nsIDocument.h"
84 : #include "nsIScrollableFrame.h"
85 : #include "nsWidgetsCID.h"
86 : #include "nsCSSAnonBoxes.h"
87 : #include "nsContainerFrame.h"
88 : #include "nsIDOMDocument.h"
89 : #include "nsIDOMElement.h"
90 : #include "nsITheme.h"
91 : #include "nsTransform2D.h"
92 : #include "nsEventStateManager.h"
93 : #include "nsEventDispatcher.h"
94 : #include "nsIDOMEvent.h"
95 : #include "nsIPrivateDOMEvent.h"
96 : #include "nsDisplayList.h"
97 : #include "mozilla/Preferences.h"
98 :
99 : // Needed for Print Preview
100 : #include "nsIURI.h"
101 :
102 : using namespace mozilla;
103 :
104 : //define DEBUG_REDRAW
105 :
106 : #define DEBUG_SPRING_SIZE 8
107 : #define DEBUG_BORDER_SIZE 2
108 : #define COIL_SIZE 8
109 :
110 : //#define TEST_SANITY
111 :
112 : #ifdef DEBUG_rods
113 : //#define DO_NOISY_REFLOW
114 : #endif
115 :
116 : #ifdef DEBUG_LAYOUT
117 : bool nsBoxFrame::gDebug = false;
118 : nsIBox* nsBoxFrame::mDebugChild = nsnull;
119 : #endif
120 :
121 : nsIFrame*
122 0 : NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager)
123 : {
124 0 : return new (aPresShell) nsBoxFrame(aPresShell, aContext, aIsRoot, aLayoutManager);
125 : }
126 :
127 : nsIFrame*
128 0 : NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
129 : {
130 0 : return new (aPresShell) nsBoxFrame(aPresShell, aContext);
131 : }
132 :
133 0 : NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame)
134 :
135 0 : nsBoxFrame::nsBoxFrame(nsIPresShell* aPresShell,
136 : nsStyleContext* aContext,
137 : bool aIsRoot,
138 : nsBoxLayout* aLayoutManager) :
139 0 : nsContainerFrame(aContext)
140 : {
141 0 : mState |= NS_STATE_IS_HORIZONTAL;
142 0 : mState |= NS_STATE_AUTO_STRETCH;
143 :
144 0 : if (aIsRoot)
145 0 : mState |= NS_STATE_IS_ROOT;
146 :
147 0 : mValign = vAlign_Top;
148 0 : mHalign = hAlign_Left;
149 :
150 : // if no layout manager specified us the static sprocket layout
151 0 : nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
152 :
153 0 : if (layout == nsnull) {
154 0 : NS_NewSprocketLayout(aPresShell, layout);
155 : }
156 :
157 0 : SetLayoutManager(layout);
158 0 : }
159 :
160 0 : nsBoxFrame::~nsBoxFrame()
161 : {
162 0 : }
163 :
164 : NS_IMETHODIMP
165 0 : nsBoxFrame::SetInitialChildList(ChildListID aListID,
166 : nsFrameList& aChildList)
167 : {
168 0 : nsresult r = nsContainerFrame::SetInitialChildList(aListID, aChildList);
169 0 : if (r == NS_OK) {
170 : // initialize our list of infos.
171 0 : nsBoxLayoutState state(PresContext());
172 0 : CheckBoxOrder(state);
173 0 : if (mLayoutManager)
174 0 : mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
175 : } else {
176 0 : NS_WARNING("Warning add child failed!!\n");
177 : }
178 :
179 0 : return r;
180 : }
181 :
182 : /* virtual */ void
183 0 : nsBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
184 : {
185 0 : nsContainerFrame::DidSetStyleContext(aOldStyleContext);
186 :
187 : // The values that CacheAttributes() computes depend on our style,
188 : // so we need to recompute them here...
189 0 : CacheAttributes();
190 0 : }
191 :
192 : /**
193 : * Initialize us. This is a good time to get the alignment of the box
194 : */
195 : NS_IMETHODIMP
196 0 : nsBoxFrame::Init(nsIContent* aContent,
197 : nsIFrame* aParent,
198 : nsIFrame* aPrevInFlow)
199 : {
200 0 : nsresult rv = nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
201 0 : NS_ENSURE_SUCCESS(rv, rv);
202 :
203 0 : MarkIntrinsicWidthsDirty();
204 :
205 0 : CacheAttributes();
206 :
207 : #ifdef DEBUG_LAYOUT
208 : // if we are root and this
209 : if (mState & NS_STATE_IS_ROOT)
210 : GetDebugPref(GetPresContext());
211 : #endif
212 :
213 0 : UpdateMouseThrough();
214 :
215 : // register access key
216 0 : rv = RegUnregAccessKey(true);
217 :
218 0 : return rv;
219 : }
220 :
221 0 : void nsBoxFrame::UpdateMouseThrough()
222 : {
223 0 : if (mContent) {
224 : static nsIContent::AttrValuesArray strings[] =
225 : {&nsGkAtoms::never, &nsGkAtoms::always, nsnull};
226 0 : switch (mContent->FindAttrValueIn(kNameSpaceID_None,
227 0 : nsGkAtoms::mousethrough, strings, eCaseMatters)) {
228 0 : case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break;
229 0 : case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break;
230 : case 2: {
231 0 : RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
232 0 : RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
233 0 : break;
234 : }
235 : }
236 : }
237 0 : }
238 :
239 : void
240 0 : nsBoxFrame::CacheAttributes()
241 : {
242 : /*
243 : printf("Caching: ");
244 : DumpBox(stdout);
245 : printf("\n");
246 : */
247 :
248 0 : mValign = vAlign_Top;
249 0 : mHalign = hAlign_Left;
250 :
251 0 : bool orient = false;
252 0 : GetInitialOrientation(orient);
253 0 : if (orient)
254 0 : mState |= NS_STATE_IS_HORIZONTAL;
255 : else
256 0 : mState &= ~NS_STATE_IS_HORIZONTAL;
257 :
258 0 : bool normal = true;
259 0 : GetInitialDirection(normal);
260 0 : if (normal)
261 0 : mState |= NS_STATE_IS_DIRECTION_NORMAL;
262 : else
263 0 : mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
264 :
265 0 : GetInitialVAlignment(mValign);
266 0 : GetInitialHAlignment(mHalign);
267 :
268 0 : bool equalSize = false;
269 0 : GetInitialEqualSize(equalSize);
270 0 : if (equalSize)
271 0 : mState |= NS_STATE_EQUAL_SIZE;
272 : else
273 0 : mState &= ~NS_STATE_EQUAL_SIZE;
274 :
275 0 : bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
276 0 : GetInitialAutoStretch(autostretch);
277 0 : if (autostretch)
278 0 : mState |= NS_STATE_AUTO_STRETCH;
279 : else
280 0 : mState &= ~NS_STATE_AUTO_STRETCH;
281 :
282 :
283 : #ifdef DEBUG_LAYOUT
284 : bool debug = mState & NS_STATE_SET_TO_DEBUG;
285 : bool debugSet = GetInitialDebug(debug);
286 : if (debugSet) {
287 : mState |= NS_STATE_DEBUG_WAS_SET;
288 : if (debug)
289 : mState |= NS_STATE_SET_TO_DEBUG;
290 : else
291 : mState &= ~NS_STATE_SET_TO_DEBUG;
292 : } else {
293 : mState &= ~NS_STATE_DEBUG_WAS_SET;
294 : }
295 : #endif
296 0 : }
297 :
298 : #ifdef DEBUG_LAYOUT
299 : bool
300 : nsBoxFrame::GetInitialDebug(bool& aDebug)
301 : {
302 : if (!GetContent())
303 : return false;
304 :
305 : static nsIContent::AttrValuesArray strings[] =
306 : {&nsGkAtoms::_false, &nsGkAtoms::_true, nsnull};
307 : PRInt32 index = GetContent()->FindAttrValueIn(kNameSpaceID_None,
308 : nsGkAtoms::debug, strings, eCaseMatters);
309 : if (index >= 0) {
310 : aDebug = index == 1;
311 : return true;
312 : }
313 :
314 : return false;
315 : }
316 : #endif
317 :
318 : bool
319 0 : nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign)
320 : {
321 0 : if (!GetContent())
322 0 : return false;
323 :
324 : // XXXdwh Everything inside this if statement is deprecated code.
325 : static nsIContent::AttrValuesArray alignStrings[] =
326 : {&nsGkAtoms::left, &nsGkAtoms::right, nsnull};
327 : static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
328 0 : PRInt32 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
329 0 : alignStrings, eCaseMatters);
330 0 : if (index >= 0) {
331 0 : aHalign = alignValues[index];
332 0 : return true;
333 : }
334 :
335 : // Now that the deprecated stuff is out of the way, we move on to check the appropriate
336 : // attribute. For horizontal boxes, we are checking the PACK attribute. For vertical boxes
337 : // we are checking the ALIGN attribute.
338 0 : nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
339 : static nsIContent::AttrValuesArray strings[] =
340 : {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nsnull};
341 : static const Halignment values[] =
342 : {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right};
343 0 : index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
344 0 : strings, eCaseMatters);
345 :
346 0 : if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
347 : // The attr was present but had a nonsensical value. Revert to the default.
348 0 : return false;
349 : }
350 0 : if (index > 0) {
351 0 : aHalign = values[index];
352 0 : return true;
353 : }
354 :
355 : // Now that we've checked for the attribute it's time to check CSS. For
356 : // horizontal boxes we're checking PACK. For vertical boxes we are checking
357 : // ALIGN.
358 0 : const nsStyleXUL* boxInfo = GetStyleXUL();
359 0 : if (IsHorizontal()) {
360 0 : switch (boxInfo->mBoxPack) {
361 : case NS_STYLE_BOX_PACK_START:
362 0 : aHalign = nsBoxFrame::hAlign_Left;
363 0 : return true;
364 : case NS_STYLE_BOX_PACK_CENTER:
365 0 : aHalign = nsBoxFrame::hAlign_Center;
366 0 : return true;
367 : case NS_STYLE_BOX_PACK_END:
368 0 : aHalign = nsBoxFrame::hAlign_Right;
369 0 : return true;
370 : default: // Nonsensical value. Just bail.
371 0 : return false;
372 : }
373 : }
374 : else {
375 0 : switch (boxInfo->mBoxAlign) {
376 : case NS_STYLE_BOX_ALIGN_START:
377 0 : aHalign = nsBoxFrame::hAlign_Left;
378 0 : return true;
379 : case NS_STYLE_BOX_ALIGN_CENTER:
380 0 : aHalign = nsBoxFrame::hAlign_Center;
381 0 : return true;
382 : case NS_STYLE_BOX_ALIGN_END:
383 0 : aHalign = nsBoxFrame::hAlign_Right;
384 0 : return true;
385 : default: // Nonsensical value. Just bail.
386 0 : return false;
387 : }
388 : }
389 :
390 : return false;
391 : }
392 :
393 : bool
394 0 : nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign)
395 : {
396 0 : if (!GetContent())
397 0 : return false;
398 :
399 : static nsIContent::AttrValuesArray valignStrings[] =
400 : {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nsnull};
401 : static const Valignment valignValues[] =
402 : {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom};
403 0 : PRInt32 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
404 0 : valignStrings, eCaseMatters);
405 0 : if (index >= 0) {
406 0 : aValign = valignValues[index];
407 0 : return true;
408 : }
409 :
410 : // Now that the deprecated stuff is out of the way, we move on to check the appropriate
411 : // attribute. For horizontal boxes, we are checking the ALIGN attribute. For vertical boxes
412 : // we are checking the PACK attribute.
413 0 : nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
414 : static nsIContent::AttrValuesArray strings[] =
415 : {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center,
416 : &nsGkAtoms::baseline, &nsGkAtoms::end, nsnull};
417 : static const Valignment values[] =
418 : {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom};
419 0 : index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
420 0 : strings, eCaseMatters);
421 0 : if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
422 : // The attr was present but had a nonsensical value. Revert to the default.
423 0 : return false;
424 : }
425 0 : if (index > 0) {
426 0 : aValign = values[index];
427 0 : return true;
428 : }
429 :
430 : // Now that we've checked for the attribute it's time to check CSS. For
431 : // horizontal boxes we're checking ALIGN. For vertical boxes we are checking
432 : // PACK.
433 0 : const nsStyleXUL* boxInfo = GetStyleXUL();
434 0 : if (IsHorizontal()) {
435 0 : switch (boxInfo->mBoxAlign) {
436 : case NS_STYLE_BOX_ALIGN_START:
437 0 : aValign = nsBoxFrame::vAlign_Top;
438 0 : return true;
439 : case NS_STYLE_BOX_ALIGN_CENTER:
440 0 : aValign = nsBoxFrame::vAlign_Middle;
441 0 : return true;
442 : case NS_STYLE_BOX_ALIGN_BASELINE:
443 0 : aValign = nsBoxFrame::vAlign_BaseLine;
444 0 : return true;
445 : case NS_STYLE_BOX_ALIGN_END:
446 0 : aValign = nsBoxFrame::vAlign_Bottom;
447 0 : return true;
448 : default: // Nonsensical value. Just bail.
449 0 : return false;
450 : }
451 : }
452 : else {
453 0 : switch (boxInfo->mBoxPack) {
454 : case NS_STYLE_BOX_PACK_START:
455 0 : aValign = nsBoxFrame::vAlign_Top;
456 0 : return true;
457 : case NS_STYLE_BOX_PACK_CENTER:
458 0 : aValign = nsBoxFrame::vAlign_Middle;
459 0 : return true;
460 : case NS_STYLE_BOX_PACK_END:
461 0 : aValign = nsBoxFrame::vAlign_Bottom;
462 0 : return true;
463 : default: // Nonsensical value. Just bail.
464 0 : return false;
465 : }
466 : }
467 :
468 : return false;
469 : }
470 :
471 : void
472 0 : nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal)
473 : {
474 : // see if we are a vertical or horizontal box.
475 0 : if (!GetContent())
476 0 : return;
477 :
478 : // Check the style system first.
479 0 : const nsStyleXUL* boxInfo = GetStyleXUL();
480 0 : if (boxInfo->mBoxOrient == NS_STYLE_BOX_ORIENT_HORIZONTAL)
481 0 : aIsHorizontal = true;
482 : else
483 0 : aIsHorizontal = false;
484 :
485 : // Now see if we have an attribute. The attribute overrides
486 : // the style system value.
487 : static nsIContent::AttrValuesArray strings[] =
488 : {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nsnull};
489 0 : PRInt32 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient,
490 0 : strings, eCaseMatters);
491 0 : if (index >= 0) {
492 0 : aIsHorizontal = index == 1;
493 : }
494 : }
495 :
496 : void
497 0 : nsBoxFrame::GetInitialDirection(bool& aIsNormal)
498 : {
499 0 : if (!GetContent())
500 0 : return;
501 :
502 0 : if (IsHorizontal()) {
503 : // For horizontal boxes only, we initialize our value based off the CSS 'direction' property.
504 : // This means that BiDI users will end up with horizontally inverted chrome.
505 0 : aIsNormal = (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we.
506 : }
507 : else
508 0 : aIsNormal = true; // Assume a normal direction in the vertical case.
509 :
510 : // Now check the style system to see if we should invert aIsNormal.
511 0 : const nsStyleXUL* boxInfo = GetStyleXUL();
512 0 : if (boxInfo->mBoxDirection == NS_STYLE_BOX_DIRECTION_REVERSE)
513 0 : aIsNormal = !aIsNormal; // Invert our direction.
514 :
515 : // Now see if we have an attribute. The attribute overrides
516 : // the style system value.
517 : static nsIContent::AttrValuesArray strings[] =
518 : {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nsnull};
519 0 : PRInt32 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir,
520 0 : strings, eCaseMatters);
521 0 : if (index >= 0) {
522 0 : bool values[] = {!aIsNormal, true, false};
523 0 : aIsNormal = values[index];
524 : }
525 : }
526 :
527 : /* Returns true if it was set.
528 : */
529 : bool
530 0 : nsBoxFrame::GetInitialEqualSize(bool& aEqualSize)
531 : {
532 : // see if we are a vertical or horizontal box.
533 0 : if (!GetContent())
534 0 : return false;
535 :
536 0 : if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::equalsize,
537 0 : nsGkAtoms::always, eCaseMatters)) {
538 0 : aEqualSize = true;
539 0 : return true;
540 : }
541 :
542 0 : return false;
543 : }
544 :
545 : /* Returns true if it was set.
546 : */
547 : bool
548 0 : nsBoxFrame::GetInitialAutoStretch(bool& aStretch)
549 : {
550 0 : if (!GetContent())
551 0 : return false;
552 :
553 : // Check the align attribute.
554 : static nsIContent::AttrValuesArray strings[] =
555 : {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nsnull};
556 0 : PRInt32 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
557 0 : strings, eCaseMatters);
558 0 : if (index != nsIContent::ATTR_MISSING && index != 0) {
559 0 : aStretch = index == 1;
560 0 : return true;
561 : }
562 :
563 : // Check the CSS box-align property.
564 0 : const nsStyleXUL* boxInfo = GetStyleXUL();
565 0 : aStretch = (boxInfo->mBoxAlign == NS_STYLE_BOX_ALIGN_STRETCH);
566 :
567 0 : return true;
568 : }
569 :
570 : NS_IMETHODIMP
571 0 : nsBoxFrame::DidReflow(nsPresContext* aPresContext,
572 : const nsHTMLReflowState* aReflowState,
573 : nsDidReflowStatus aStatus)
574 : {
575 : nsFrameState preserveBits =
576 0 : mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
577 0 : nsresult rv = nsFrame::DidReflow(aPresContext, aReflowState, aStatus);
578 0 : mState |= preserveBits;
579 0 : return rv;
580 : }
581 :
582 : bool
583 0 : nsBoxFrame::HonorPrintBackgroundSettings()
584 : {
585 0 : return (!mContent || !mContent->IsInNativeAnonymousSubtree()) &&
586 0 : nsContainerFrame::HonorPrintBackgroundSettings();
587 : }
588 :
589 : #ifdef DO_NOISY_REFLOW
590 : static int myCounter = 0;
591 : static void printSize(char * aDesc, nscoord aSize)
592 : {
593 : printf(" %s: ", aDesc);
594 : if (aSize == NS_UNCONSTRAINEDSIZE) {
595 : printf("UC");
596 : } else {
597 : printf("%d", aSize);
598 : }
599 : }
600 : #endif
601 :
602 : /* virtual */ nscoord
603 0 : nsBoxFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
604 : {
605 : nscoord result;
606 0 : DISPLAY_MIN_WIDTH(this, result);
607 :
608 0 : nsBoxLayoutState state(PresContext(), aRenderingContext);
609 0 : nsSize minSize = GetMinSize(state);
610 :
611 : // GetMinSize returns border-box width, and we want to return content
612 : // width. Since Reflow uses the reflow state's border and padding, we
613 : // actually just want to subtract what GetMinSize added, which is the
614 : // result of GetBorderAndPadding.
615 0 : nsMargin bp;
616 0 : GetBorderAndPadding(bp);
617 :
618 0 : result = minSize.width - bp.LeftRight();
619 0 : result = NS_MAX(result, 0);
620 :
621 0 : return result;
622 : }
623 :
624 : /* virtual */ nscoord
625 0 : nsBoxFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
626 : {
627 : nscoord result;
628 0 : DISPLAY_PREF_WIDTH(this, result);
629 :
630 0 : nsBoxLayoutState state(PresContext(), aRenderingContext);
631 0 : nsSize prefSize = GetPrefSize(state);
632 :
633 : // GetPrefSize returns border-box width, and we want to return content
634 : // width. Since Reflow uses the reflow state's border and padding, we
635 : // actually just want to subtract what GetPrefSize added, which is the
636 : // result of GetBorderAndPadding.
637 0 : nsMargin bp;
638 0 : GetBorderAndPadding(bp);
639 :
640 0 : result = prefSize.width - bp.LeftRight();
641 0 : result = NS_MAX(result, 0);
642 :
643 0 : return result;
644 : }
645 :
646 : NS_IMETHODIMP
647 0 : nsBoxFrame::Reflow(nsPresContext* aPresContext,
648 : nsHTMLReflowMetrics& aDesiredSize,
649 : const nsHTMLReflowState& aReflowState,
650 : nsReflowStatus& aStatus)
651 : {
652 : // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
653 : // in sync, if the changes are applicable there.
654 :
655 0 : DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
656 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
657 :
658 0 : NS_ASSERTION(aReflowState.ComputedWidth() >=0 &&
659 : aReflowState.ComputedHeight() >= 0, "Computed Size < 0");
660 :
661 : #ifdef DO_NOISY_REFLOW
662 : printf("\n-------------Starting BoxFrame Reflow ----------------------------\n");
663 : printf("%p ** nsBF::Reflow %d ", this, myCounter++);
664 :
665 : printSize("AW", aReflowState.availableWidth);
666 : printSize("AH", aReflowState.availableHeight);
667 : printSize("CW", aReflowState.ComputedWidth());
668 : printSize("CH", aReflowState.ComputedHeight());
669 :
670 : printf(" *\n");
671 :
672 : #endif
673 :
674 0 : aStatus = NS_FRAME_COMPLETE;
675 :
676 : // create the layout state
677 : nsBoxLayoutState state(aPresContext, aReflowState.rendContext,
678 0 : &aReflowState, aReflowState.mReflowDepth);
679 :
680 0 : nsSize computedSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight());
681 :
682 0 : nsMargin m;
683 0 : m = aReflowState.mComputedBorderPadding;
684 : // GetBorderAndPadding(m);
685 :
686 0 : nsSize prefSize(0,0);
687 :
688 : // if we are told to layout intrinsic then get our preferred size.
689 0 : NS_ASSERTION(computedSize.width != NS_INTRINSICSIZE,
690 : "computed width should always be computed");
691 0 : if (computedSize.height == NS_INTRINSICSIZE) {
692 0 : prefSize = GetPrefSize(state);
693 0 : nsSize minSize = GetMinSize(state);
694 0 : nsSize maxSize = GetMaxSize(state);
695 : // XXXbz isn't GetPrefSize supposed to bounds-check for us?
696 0 : prefSize = BoundsCheck(minSize, prefSize, maxSize);
697 : }
698 :
699 : // get our desiredSize
700 0 : computedSize.width += m.left + m.right;
701 :
702 0 : if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
703 0 : computedSize.height = prefSize.height;
704 : // prefSize is border-box, so we need to figure out the right
705 : // length to apply our min/max constraints to.
706 0 : nscoord outsideBoxSizing = 0;
707 0 : switch (GetStylePosition()->mBoxSizing) {
708 : case NS_STYLE_BOX_SIZING_CONTENT:
709 0 : outsideBoxSizing = aReflowState.mComputedBorderPadding.TopBottom();
710 : // fall through
711 : case NS_STYLE_BOX_SIZING_PADDING:
712 0 : outsideBoxSizing -= aReflowState.mComputedPadding.TopBottom();
713 0 : break;
714 : }
715 0 : computedSize.height -= outsideBoxSizing;
716 : // Note: might be negative now, but that's OK because min-width is
717 : // never negative.
718 0 : aReflowState.ApplyMinMaxConstraints(nsnull, &computedSize.height);
719 0 : computedSize.height += outsideBoxSizing;
720 : } else {
721 0 : computedSize.height += m.top + m.bottom;
722 : }
723 :
724 0 : nsRect r(mRect.x, mRect.y, computedSize.width, computedSize.height);
725 :
726 0 : SetBounds(state, r);
727 :
728 : // layout our children
729 0 : Layout(state);
730 :
731 : // ok our child could have gotten bigger. So lets get its bounds
732 :
733 : // get the ascent
734 0 : nscoord ascent = mRect.height;
735 :
736 : // getting the ascent could be a lot of work. Don't get it if
737 : // we are the root. The viewport doesn't care about it.
738 0 : if (!(mState & NS_STATE_IS_ROOT)) {
739 0 : ascent = GetBoxAscent(state);
740 : }
741 :
742 0 : aDesiredSize.width = mRect.width;
743 0 : aDesiredSize.height = mRect.height;
744 0 : aDesiredSize.ascent = ascent;
745 :
746 0 : aDesiredSize.mOverflowAreas = GetOverflowAreas();
747 :
748 : #ifdef DO_NOISY_REFLOW
749 : {
750 : printf("%p ** nsBF(done) W:%d H:%d ", this, aDesiredSize.width, aDesiredSize.height);
751 :
752 : if (maxElementSize) {
753 : printf("MW:%d\n", *maxElementWidth);
754 : } else {
755 : printf("MW:?\n");
756 : }
757 :
758 : }
759 : #endif
760 :
761 0 : ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
762 :
763 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
764 0 : return NS_OK;
765 : }
766 :
767 : nsSize
768 0 : nsBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
769 : {
770 0 : NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
771 : "must have rendering context");
772 :
773 0 : nsSize size(0,0);
774 0 : DISPLAY_PREF_SIZE(this, size);
775 0 : if (!DoesNeedRecalc(mPrefSize)) {
776 0 : return mPrefSize;
777 : }
778 :
779 : #ifdef DEBUG_LAYOUT
780 : PropagateDebug(aBoxLayoutState);
781 : #endif
782 :
783 0 : if (IsCollapsed())
784 0 : return size;
785 :
786 : // if the size was not completely redefined in CSS then ask our children
787 : bool widthSet, heightSet;
788 0 : if (!nsIBox::AddCSSPrefSize(this, size, widthSet, heightSet))
789 : {
790 0 : if (mLayoutManager) {
791 0 : nsSize layoutSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState);
792 0 : if (!widthSet)
793 0 : size.width = layoutSize.width;
794 0 : if (!heightSet)
795 0 : size.height = layoutSize.height;
796 : }
797 : else {
798 0 : size = nsBox::GetPrefSize(aBoxLayoutState);
799 : }
800 : }
801 :
802 0 : nsSize minSize = GetMinSize(aBoxLayoutState);
803 0 : nsSize maxSize = GetMaxSize(aBoxLayoutState);
804 0 : mPrefSize = BoundsCheck(minSize, size, maxSize);
805 :
806 0 : return mPrefSize;
807 : }
808 :
809 : nscoord
810 0 : nsBoxFrame::GetBoxAscent(nsBoxLayoutState& aBoxLayoutState)
811 : {
812 0 : if (!DoesNeedRecalc(mAscent))
813 0 : return mAscent;
814 :
815 : #ifdef DEBUG_LAYOUT
816 : PropagateDebug(aBoxLayoutState);
817 : #endif
818 :
819 0 : if (IsCollapsed())
820 0 : return 0;
821 :
822 0 : if (mLayoutManager)
823 0 : mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
824 : else
825 0 : mAscent = nsBox::GetBoxAscent(aBoxLayoutState);
826 :
827 0 : return mAscent;
828 : }
829 :
830 : nsSize
831 0 : nsBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
832 : {
833 0 : NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
834 : "must have rendering context");
835 :
836 0 : nsSize size(0,0);
837 0 : DISPLAY_MIN_SIZE(this, size);
838 0 : if (!DoesNeedRecalc(mMinSize)) {
839 0 : return mMinSize;
840 : }
841 :
842 : #ifdef DEBUG_LAYOUT
843 : PropagateDebug(aBoxLayoutState);
844 : #endif
845 :
846 0 : if (IsCollapsed())
847 0 : return size;
848 :
849 : // if the size was not completely redefined in CSS then ask our children
850 : bool widthSet, heightSet;
851 0 : if (!nsIBox::AddCSSMinSize(aBoxLayoutState, this, size, widthSet, heightSet))
852 : {
853 0 : if (mLayoutManager) {
854 0 : nsSize layoutSize = mLayoutManager->GetMinSize(this, aBoxLayoutState);
855 0 : if (!widthSet)
856 0 : size.width = layoutSize.width;
857 0 : if (!heightSet)
858 0 : size.height = layoutSize.height;
859 : }
860 : else {
861 0 : size = nsBox::GetMinSize(aBoxLayoutState);
862 : }
863 : }
864 :
865 0 : mMinSize = size;
866 :
867 0 : return size;
868 : }
869 :
870 : nsSize
871 0 : nsBoxFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState)
872 : {
873 0 : NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
874 : "must have rendering context");
875 :
876 0 : nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
877 0 : DISPLAY_MAX_SIZE(this, size);
878 0 : if (!DoesNeedRecalc(mMaxSize)) {
879 0 : return mMaxSize;
880 : }
881 :
882 : #ifdef DEBUG_LAYOUT
883 : PropagateDebug(aBoxLayoutState);
884 : #endif
885 :
886 0 : if (IsCollapsed())
887 0 : return size;
888 :
889 : // if the size was not completely redefined in CSS then ask our children
890 : bool widthSet, heightSet;
891 0 : if (!nsIBox::AddCSSMaxSize(this, size, widthSet, heightSet))
892 : {
893 0 : if (mLayoutManager) {
894 0 : nsSize layoutSize = mLayoutManager->GetMaxSize(this, aBoxLayoutState);
895 0 : if (!widthSet)
896 0 : size.width = layoutSize.width;
897 0 : if (!heightSet)
898 0 : size.height = layoutSize.height;
899 : }
900 : else {
901 0 : size = nsBox::GetMaxSize(aBoxLayoutState);
902 : }
903 : }
904 :
905 0 : mMaxSize = size;
906 :
907 0 : return size;
908 : }
909 :
910 : nscoord
911 0 : nsBoxFrame::GetFlex(nsBoxLayoutState& aBoxLayoutState)
912 : {
913 0 : if (!DoesNeedRecalc(mFlex))
914 0 : return mFlex;
915 :
916 0 : mFlex = nsBox::GetFlex(aBoxLayoutState);
917 :
918 0 : return mFlex;
919 : }
920 :
921 : /**
922 : * If subclassing please subclass this method not layout.
923 : * layout will call this method.
924 : */
925 : NS_IMETHODIMP
926 0 : nsBoxFrame::DoLayout(nsBoxLayoutState& aState)
927 : {
928 0 : PRUint32 oldFlags = aState.LayoutFlags();
929 0 : aState.SetLayoutFlags(0);
930 :
931 0 : nsresult rv = NS_OK;
932 0 : if (mLayoutManager) {
933 0 : CoordNeedsRecalc(mAscent);
934 0 : rv = mLayoutManager->Layout(this, aState);
935 : }
936 :
937 0 : aState.SetLayoutFlags(oldFlags);
938 :
939 0 : if (HasAbsolutelyPositionedChildren()) {
940 : // Set up a |reflowState| to pass into ReflowAbsoluteFrames
941 : nsHTMLReflowState reflowState(aState.PresContext(), this,
942 : aState.GetRenderingContext(),
943 0 : nsSize(mRect.width, NS_UNCONSTRAINEDSIZE));
944 :
945 : // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
946 0 : nsHTMLReflowMetrics desiredSize;
947 0 : desiredSize.width = mRect.width;
948 0 : desiredSize.height = mRect.height;
949 :
950 : // get the ascent (cribbed from ::Reflow)
951 0 : nscoord ascent = mRect.height;
952 :
953 : // getting the ascent could be a lot of work. Don't get it if
954 : // we are the root. The viewport doesn't care about it.
955 0 : if (!(mState & NS_STATE_IS_ROOT)) {
956 0 : ascent = GetBoxAscent(aState);
957 : }
958 0 : desiredSize.ascent = ascent;
959 0 : desiredSize.mOverflowAreas = GetOverflowAreas();
960 :
961 : // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
962 : // (just a dummy value; hopefully that's OK)
963 0 : nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
964 : ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
965 0 : reflowState, reflowStatus);
966 : }
967 :
968 0 : return rv;
969 : }
970 :
971 : void
972 0 : nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
973 : {
974 : // unregister access key
975 0 : RegUnregAccessKey(false);
976 :
977 : // clean up the container box's layout manager and child boxes
978 0 : SetLayoutManager(nsnull);
979 :
980 0 : DestroyAbsoluteFrames(aDestructRoot);
981 :
982 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
983 0 : }
984 :
985 : #ifdef DEBUG_LAYOUT
986 : NS_IMETHODIMP
987 : nsBoxFrame::SetDebug(nsBoxLayoutState& aState, bool aDebug)
988 : {
989 : // see if our state matches the given debug state
990 : bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
991 : bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
992 :
993 : // if it doesn't then tell each child below us the new debug state
994 : if (debugChanged)
995 : {
996 : if (aDebug) {
997 : mState |= NS_STATE_CURRENTLY_IN_DEBUG;
998 : } else {
999 : mState &= ~NS_STATE_CURRENTLY_IN_DEBUG;
1000 : }
1001 :
1002 : SetDebugOnChildList(aState, mFirstChild, aDebug);
1003 :
1004 : MarkIntrinsicWidthsDirty();
1005 : }
1006 :
1007 : return NS_OK;
1008 : }
1009 : #endif
1010 :
1011 : /* virtual */ void
1012 0 : nsBoxFrame::MarkIntrinsicWidthsDirty()
1013 : {
1014 0 : SizeNeedsRecalc(mPrefSize);
1015 0 : SizeNeedsRecalc(mMinSize);
1016 0 : SizeNeedsRecalc(mMaxSize);
1017 0 : CoordNeedsRecalc(mFlex);
1018 0 : CoordNeedsRecalc(mAscent);
1019 :
1020 0 : if (mLayoutManager) {
1021 0 : nsBoxLayoutState state(PresContext());
1022 0 : mLayoutManager->IntrinsicWidthsDirty(this, state);
1023 : }
1024 :
1025 : // Don't call base class method, since everything it does is within an
1026 : // IsBoxWrapped check.
1027 0 : }
1028 :
1029 : NS_IMETHODIMP
1030 0 : nsBoxFrame::RemoveFrame(ChildListID aListID,
1031 : nsIFrame* aOldFrame)
1032 : {
1033 0 : NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1034 0 : nsPresContext* presContext = PresContext();
1035 0 : nsBoxLayoutState state(presContext);
1036 :
1037 : // remove the child frame
1038 0 : mFrames.RemoveFrame(aOldFrame);
1039 :
1040 : // notify the layout manager
1041 0 : if (mLayoutManager)
1042 0 : mLayoutManager->ChildrenRemoved(this, state, aOldFrame);
1043 :
1044 : // destroy the child frame
1045 0 : aOldFrame->Destroy();
1046 :
1047 : // mark us dirty and generate a reflow command
1048 0 : PresContext()->PresShell()->
1049 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1050 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
1051 0 : return NS_OK;
1052 : }
1053 :
1054 : NS_IMETHODIMP
1055 0 : nsBoxFrame::InsertFrames(ChildListID aListID,
1056 : nsIFrame* aPrevFrame,
1057 : nsFrameList& aFrameList)
1058 : {
1059 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
1060 : "inserting after sibling frame with different parent");
1061 0 : NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame),
1062 : "inserting after sibling frame not in our child list");
1063 0 : NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1064 0 : nsBoxLayoutState state(PresContext());
1065 :
1066 : // insert the child frames
1067 : const nsFrameList::Slice& newFrames =
1068 0 : mFrames.InsertFrames(this, aPrevFrame, aFrameList);
1069 :
1070 : // notify the layout manager
1071 0 : if (mLayoutManager)
1072 0 : mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
1073 :
1074 : // Make sure to check box order _after_ notifying the layout
1075 : // manager; otherwise the slice we give the layout manager will
1076 : // just be bogus. If the layout manager cares about the order, we
1077 : // just lose.
1078 0 : CheckBoxOrder(state);
1079 :
1080 : #ifdef DEBUG_LAYOUT
1081 : // if we are in debug make sure our children are in debug as well.
1082 : if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
1083 : SetDebugOnChildList(state, mFrames.FirstChild(), true);
1084 : #endif
1085 :
1086 0 : PresContext()->PresShell()->
1087 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1088 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
1089 0 : return NS_OK;
1090 : }
1091 :
1092 :
1093 : NS_IMETHODIMP
1094 0 : nsBoxFrame::AppendFrames(ChildListID aListID,
1095 : nsFrameList& aFrameList)
1096 : {
1097 0 : NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1098 0 : nsBoxLayoutState state(PresContext());
1099 :
1100 : // append the new frames
1101 0 : const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);
1102 :
1103 : // notify the layout manager
1104 0 : if (mLayoutManager)
1105 0 : mLayoutManager->ChildrenAppended(this, state, newFrames);
1106 :
1107 : // Make sure to check box order _after_ notifying the layout
1108 : // manager; otherwise the slice we give the layout manager will
1109 : // just be bogus. If the layout manager cares about the order, we
1110 : // just lose.
1111 0 : CheckBoxOrder(state);
1112 :
1113 : #ifdef DEBUG_LAYOUT
1114 : // if we are in debug make sure our children are in debug as well.
1115 : if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
1116 : SetDebugOnChildList(state, mFrames.FirstChild(), true);
1117 : #endif
1118 :
1119 : // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
1120 0 : if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1121 0 : PresContext()->PresShell()->
1122 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1123 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
1124 : }
1125 0 : return NS_OK;
1126 : }
1127 :
1128 : /* virtual */ nsIFrame*
1129 0 : nsBoxFrame::GetContentInsertionFrame()
1130 : {
1131 0 : if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
1132 0 : return GetFirstPrincipalChild()->GetContentInsertionFrame();
1133 0 : return nsContainerFrame::GetContentInsertionFrame();
1134 : }
1135 :
1136 : NS_IMETHODIMP
1137 0 : nsBoxFrame::AttributeChanged(PRInt32 aNameSpaceID,
1138 : nsIAtom* aAttribute,
1139 : PRInt32 aModType)
1140 : {
1141 : nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
1142 0 : aModType);
1143 :
1144 : // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
1145 : // <window>.
1146 0 : nsIAtom *tag = mContent->Tag();
1147 0 : if ((tag == nsGkAtoms::window ||
1148 : tag == nsGkAtoms::page ||
1149 : tag == nsGkAtoms::dialog ||
1150 : tag == nsGkAtoms::wizard) &&
1151 : (nsGkAtoms::width == aAttribute ||
1152 : nsGkAtoms::height == aAttribute ||
1153 : nsGkAtoms::screenX == aAttribute ||
1154 : nsGkAtoms::screenY == aAttribute ||
1155 : nsGkAtoms::sizemode == aAttribute)) {
1156 0 : return rv;
1157 : }
1158 :
1159 0 : if (aAttribute == nsGkAtoms::width ||
1160 : aAttribute == nsGkAtoms::height ||
1161 : aAttribute == nsGkAtoms::align ||
1162 : aAttribute == nsGkAtoms::valign ||
1163 : aAttribute == nsGkAtoms::left ||
1164 : aAttribute == nsGkAtoms::top ||
1165 : aAttribute == nsGkAtoms::right ||
1166 : aAttribute == nsGkAtoms::bottom ||
1167 : aAttribute == nsGkAtoms::start ||
1168 : aAttribute == nsGkAtoms::end ||
1169 : aAttribute == nsGkAtoms::minwidth ||
1170 : aAttribute == nsGkAtoms::maxwidth ||
1171 : aAttribute == nsGkAtoms::minheight ||
1172 : aAttribute == nsGkAtoms::maxheight ||
1173 : aAttribute == nsGkAtoms::flex ||
1174 : aAttribute == nsGkAtoms::orient ||
1175 : aAttribute == nsGkAtoms::pack ||
1176 : aAttribute == nsGkAtoms::dir ||
1177 : aAttribute == nsGkAtoms::mousethrough ||
1178 : aAttribute == nsGkAtoms::equalsize) {
1179 :
1180 0 : if (aAttribute == nsGkAtoms::align ||
1181 : aAttribute == nsGkAtoms::valign ||
1182 : aAttribute == nsGkAtoms::orient ||
1183 : aAttribute == nsGkAtoms::pack ||
1184 : #ifdef DEBUG_LAYOUT
1185 : aAttribute == nsGkAtoms::debug ||
1186 : #endif
1187 : aAttribute == nsGkAtoms::dir) {
1188 :
1189 0 : mValign = nsBoxFrame::vAlign_Top;
1190 0 : mHalign = nsBoxFrame::hAlign_Left;
1191 :
1192 0 : bool orient = true;
1193 0 : GetInitialOrientation(orient);
1194 0 : if (orient)
1195 0 : mState |= NS_STATE_IS_HORIZONTAL;
1196 : else
1197 0 : mState &= ~NS_STATE_IS_HORIZONTAL;
1198 :
1199 0 : bool normal = true;
1200 0 : GetInitialDirection(normal);
1201 0 : if (normal)
1202 0 : mState |= NS_STATE_IS_DIRECTION_NORMAL;
1203 : else
1204 0 : mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
1205 :
1206 0 : GetInitialVAlignment(mValign);
1207 0 : GetInitialHAlignment(mHalign);
1208 :
1209 0 : bool equalSize = false;
1210 0 : GetInitialEqualSize(equalSize);
1211 0 : if (equalSize)
1212 0 : mState |= NS_STATE_EQUAL_SIZE;
1213 : else
1214 0 : mState &= ~NS_STATE_EQUAL_SIZE;
1215 :
1216 : #ifdef DEBUG_LAYOUT
1217 : bool debug = mState & NS_STATE_SET_TO_DEBUG;
1218 : bool debugSet = GetInitialDebug(debug);
1219 : if (debugSet) {
1220 : mState |= NS_STATE_DEBUG_WAS_SET;
1221 :
1222 : if (debug)
1223 : mState |= NS_STATE_SET_TO_DEBUG;
1224 : else
1225 : mState &= ~NS_STATE_SET_TO_DEBUG;
1226 : } else {
1227 : mState &= ~NS_STATE_DEBUG_WAS_SET;
1228 : }
1229 : #endif
1230 :
1231 0 : bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
1232 0 : GetInitialAutoStretch(autostretch);
1233 0 : if (autostretch)
1234 0 : mState |= NS_STATE_AUTO_STRETCH;
1235 : else
1236 0 : mState &= ~NS_STATE_AUTO_STRETCH;
1237 : }
1238 0 : else if (aAttribute == nsGkAtoms::left ||
1239 : aAttribute == nsGkAtoms::top ||
1240 : aAttribute == nsGkAtoms::right ||
1241 : aAttribute == nsGkAtoms::bottom ||
1242 : aAttribute == nsGkAtoms::start ||
1243 : aAttribute == nsGkAtoms::end) {
1244 0 : mState &= ~NS_STATE_STACK_NOT_POSITIONED;
1245 : }
1246 0 : else if (aAttribute == nsGkAtoms::mousethrough) {
1247 0 : UpdateMouseThrough();
1248 : }
1249 :
1250 0 : PresContext()->PresShell()->
1251 0 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
1252 : }
1253 0 : else if (aAttribute == nsGkAtoms::ordinal) {
1254 0 : nsBoxLayoutState state(PresContext());
1255 0 : nsIBox* parent = GetParentBox();
1256 : // If our parent is not a box, there's not much we can do... but in that
1257 : // case our ordinal doesn't matter anyway, so that's ok.
1258 : // Also don't bother with popup frames since they are kept on the
1259 : // kPopupList and RelayoutChildAtOrdinal() only handles
1260 : // principal children.
1261 0 : if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1262 0 : GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_POPUP) {
1263 0 : parent->RelayoutChildAtOrdinal(state, this);
1264 : // XXXldb Should this instead be a tree change on the child or parent?
1265 0 : PresContext()->PresShell()->
1266 : FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
1267 0 : NS_FRAME_IS_DIRTY);
1268 : }
1269 : }
1270 : // If the accesskey changed, register for the new value
1271 : // The old value has been unregistered in nsXULElement::SetAttr
1272 0 : else if (aAttribute == nsGkAtoms::accesskey) {
1273 0 : RegUnregAccessKey(true);
1274 : }
1275 :
1276 0 : return rv;
1277 : }
1278 :
1279 : #ifdef DEBUG_LAYOUT
1280 : void
1281 : nsBoxFrame::GetDebugPref(nsPresContext* aPresContext)
1282 : {
1283 : gDebug = Preferences::GetBool("xul.debug.box");
1284 : }
1285 :
1286 : class nsDisplayXULDebug : public nsDisplayItem {
1287 : public:
1288 : nsDisplayXULDebug(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) :
1289 : nsDisplayItem(aBuilder, aFrame) {
1290 : MOZ_COUNT_CTOR(nsDisplayXULDebug);
1291 : }
1292 : #ifdef NS_BUILD_REFCNT_LOGGING
1293 : virtual ~nsDisplayXULDebug() {
1294 : MOZ_COUNT_DTOR(nsDisplayXULDebug);
1295 : }
1296 : #endif
1297 :
1298 : virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect,
1299 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
1300 : nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
1301 : static_cast<nsBoxFrame*>(mFrame)->
1302 : DisplayDebugInfoFor(this, rectCenter - ToReferenceFrame());
1303 : aOutFrames->AppendElement(this);
1304 : }
1305 : virtual void Paint(nsDisplayListBuilder* aBuilder
1306 : nsRenderingContext* aCtx);
1307 : NS_DISPLAY_DECL_NAME("XULDebug", TYPE_XUL_DEBUG)
1308 : };
1309 :
1310 : void
1311 : nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
1312 : nsRenderingContext* aCtx)
1313 : {
1314 : static_cast<nsBoxFrame*>(mFrame)->
1315 : PaintXULDebugOverlay(*aCtx, ToReferenceFrame());
1316 : }
1317 :
1318 : static void
1319 : PaintXULDebugBackground(nsIFrame* aFrame, nsRenderingContext* aCtx,
1320 : const nsRect& aDirtyRect, nsPoint aPt)
1321 : {
1322 : static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(*aCtx, aPt);
1323 : }
1324 : #endif
1325 :
1326 : nsresult
1327 0 : nsBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1328 : const nsRect& aDirtyRect,
1329 : const nsDisplayListSet& aLists)
1330 : {
1331 : // forcelayer is only supported on XUL elements with box layout
1332 : bool forceLayer =
1333 0 : GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer) &&
1334 0 : GetContent()->IsXUL();
1335 :
1336 : // Check for frames that are marked as a part of the region used
1337 : // in calculating glass margins on Windows.
1338 0 : if (GetContent()->IsXUL()) {
1339 0 : const nsStyleDisplay* styles = mStyleContext->GetStyleDisplay();
1340 0 : if (styles && styles->mAppearance == NS_THEME_WIN_EXCLUDE_GLASS) {
1341 0 : nsRect rect = mRect + aBuilder->ToReferenceFrame(GetParent());
1342 0 : aBuilder->AddExcludedGlassRegion(rect);
1343 : }
1344 : }
1345 :
1346 0 : nsDisplayListCollection tempLists;
1347 0 : const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;
1348 :
1349 0 : nsresult rv = DisplayBorderBackgroundOutline(aBuilder, destination);
1350 0 : NS_ENSURE_SUCCESS(rv, rv);
1351 :
1352 : #ifdef DEBUG_LAYOUT
1353 : if (mState & NS_STATE_CURRENTLY_IN_DEBUG) {
1354 : rv = destination.BorderBackground()->AppendNewToTop(new (aBuilder)
1355 : nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground,
1356 : "XULDebugBackground"));
1357 : NS_ENSURE_SUCCESS(rv, rv);
1358 : rv = destination.Outlines()->AppendNewToTop(new (aBuilder)
1359 : nsDisplayXULDebug(aBuilder, this));
1360 : NS_ENSURE_SUCCESS(rv, rv);
1361 : }
1362 : #endif
1363 :
1364 0 : rv = BuildDisplayListForChildren(aBuilder, aDirtyRect, destination);
1365 0 : NS_ENSURE_SUCCESS(rv, rv);
1366 :
1367 : // see if we have to draw a selection frame around this container
1368 0 : rv = DisplaySelectionOverlay(aBuilder, destination.Content());
1369 0 : NS_ENSURE_SUCCESS(rv, rv);
1370 :
1371 0 : if (forceLayer) {
1372 : // This is a bit of a hack. Collect up all descendant display items
1373 : // and merge them into a single Content() list. This can cause us
1374 : // to violate CSS stacking order, but forceLayer is a magic
1375 : // XUL-only extension anyway.
1376 0 : nsDisplayList masterList;
1377 0 : masterList.AppendToTop(tempLists.BorderBackground());
1378 0 : masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
1379 0 : masterList.AppendToTop(tempLists.Floats());
1380 0 : masterList.AppendToTop(tempLists.Content());
1381 0 : masterList.AppendToTop(tempLists.PositionedDescendants());
1382 0 : masterList.AppendToTop(tempLists.Outlines());
1383 : // Wrap the list to make it its own layer
1384 : rv = aLists.Content()->AppendNewToTop(new (aBuilder)
1385 0 : nsDisplayOwnLayer(aBuilder, this, &masterList));
1386 0 : NS_ENSURE_SUCCESS(rv, rv);
1387 : }
1388 0 : return NS_OK;
1389 : }
1390 :
1391 : NS_IMETHODIMP
1392 0 : nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
1393 : const nsRect& aDirtyRect,
1394 : const nsDisplayListSet& aLists)
1395 : {
1396 0 : nsIFrame* kid = mFrames.FirstChild();
1397 : // Put each child's background onto the BlockBorderBackgrounds list
1398 : // to emulate the existing two-layer XUL painting scheme.
1399 0 : nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
1400 : // The children should be in the right order
1401 0 : while (kid) {
1402 0 : nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set);
1403 0 : NS_ENSURE_SUCCESS(rv, rv);
1404 0 : kid = kid->GetNextSibling();
1405 : }
1406 0 : return NS_OK;
1407 : }
1408 :
1409 : // REVIEW: PaintChildren did a few things none of which are a big deal
1410 : // anymore:
1411 : // * Paint some debugging rects for this frame.
1412 : // This is done by nsDisplayXULDebugBackground, which goes in the
1413 : // BorderBackground() layer so it isn't clipped by OVERFLOW_CLIP.
1414 : // * Apply OVERFLOW_CLIP to the children.
1415 : // This is now in nsFrame::BuildDisplayListForStackingContext/Child.
1416 : // * Actually paint the children.
1417 : // Moved to BuildDisplayList.
1418 : // * Paint per-kid debug information.
1419 : // This is done by nsDisplayXULDebug, which is in the Outlines()
1420 : // layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP,
1421 : // whereas it did used to respect OVERFLOW_CLIP, but too bad.
1422 : #ifdef DEBUG_LAYOUT
1423 : void
1424 : nsBoxFrame::PaintXULDebugBackground(nsRenderingContext& aRenderingContext,
1425 : nsPoint aPt)
1426 : {
1427 : nsMargin border;
1428 : GetBorder(border);
1429 :
1430 : nsMargin debugBorder;
1431 : nsMargin debugMargin;
1432 : nsMargin debugPadding;
1433 :
1434 : bool isHorizontal = IsHorizontal();
1435 :
1436 : GetDebugBorder(debugBorder);
1437 : PixelMarginToTwips(GetPresContext(), debugBorder);
1438 :
1439 : GetDebugMargin(debugMargin);
1440 : PixelMarginToTwips(GetPresContext(), debugMargin);
1441 :
1442 : GetDebugPadding(debugPadding);
1443 : PixelMarginToTwips(GetPresContext(), debugPadding);
1444 :
1445 : nsRect inner(mRect);
1446 : inner.MoveTo(aPt);
1447 : inner.Deflate(debugMargin);
1448 : inner.Deflate(border);
1449 : //nsRect borderRect(inner);
1450 :
1451 : nscolor color;
1452 : if (isHorizontal) {
1453 : color = NS_RGB(0,0,255);
1454 : } else {
1455 : color = NS_RGB(255,0,0);
1456 : }
1457 :
1458 : aRenderingContext.SetColor(color);
1459 :
1460 : //left
1461 : nsRect r(inner);
1462 : r.width = debugBorder.left;
1463 : aRenderingContext.FillRect(r);
1464 :
1465 : // top
1466 : r = inner;
1467 : r.height = debugBorder.top;
1468 : aRenderingContext.FillRect(r);
1469 :
1470 : //right
1471 : r = inner;
1472 : r.x = r.x + r.width - debugBorder.right;
1473 : r.width = debugBorder.right;
1474 : aRenderingContext.FillRect(r);
1475 :
1476 : //bottom
1477 : r = inner;
1478 : r.y = r.y + r.height - debugBorder.bottom;
1479 : r.height = debugBorder.bottom;
1480 : aRenderingContext.FillRect(r);
1481 :
1482 :
1483 : // if we have dirty children or we are dirty
1484 : // place a green border around us.
1485 : if (NS_SUBTREE_DIRTY(this)) {
1486 : nsRect dirtyr(inner);
1487 : aRenderingContext.SetColor(NS_RGB(0,255,0));
1488 : aRenderingContext.DrawRect(dirtyr);
1489 : aRenderingContext.SetColor(color);
1490 : }
1491 : }
1492 :
1493 : void
1494 : nsBoxFrame::PaintXULDebugOverlay(nsRenderingContext& aRenderingContext,
1495 : nsPoint aPt)
1496 : nsMargin border;
1497 : GetBorder(border);
1498 :
1499 : nsMargin debugMargin;
1500 : GetDebugMargin(debugMargin);
1501 : PixelMarginToTwips(GetPresContext(), debugMargin);
1502 :
1503 : nsRect inner(mRect);
1504 : inner.MoveTo(aPt);
1505 : inner.Deflate(debugMargin);
1506 : inner.Deflate(border);
1507 :
1508 : nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
1509 :
1510 : kid = GetChildBox();
1511 : while (nsnull != kid) {
1512 : bool isHorizontal = IsHorizontal();
1513 :
1514 : nscoord x, y, borderSize, spacerSize;
1515 :
1516 : nsRect cr(kid->mRect);
1517 : nsMargin margin;
1518 : kid->GetMargin(margin);
1519 : cr.Inflate(margin);
1520 :
1521 : if (isHorizontal)
1522 : {
1523 : cr.y = inner.y;
1524 : x = cr.x;
1525 : y = cr.y + onePixel;
1526 : spacerSize = debugBorder.top - onePixel*4;
1527 : } else {
1528 : cr.x = inner.x;
1529 : x = cr.y;
1530 : y = cr.x + onePixel;
1531 : spacerSize = debugBorder.left - onePixel*4;
1532 : }
1533 :
1534 : nsBoxLayoutState state(GetPresContext());
1535 : nscoord flex = kid->GetFlex(state);
1536 :
1537 : if (!kid->IsCollapsed()) {
1538 : aRenderingContext.SetColor(NS_RGB(255,255,255));
1539 :
1540 : if (isHorizontal)
1541 : borderSize = cr.width;
1542 : else
1543 : borderSize = cr.height;
1544 :
1545 : DrawSpacer(GetPresContext(), aRenderingContext, isHorizontal, flex, x, y, borderSize, spacerSize);
1546 : }
1547 :
1548 : kid = kid->GetNextBox();
1549 : }
1550 : }
1551 : #endif
1552 :
1553 : #ifdef DEBUG_LAYOUT
1554 : void
1555 : nsBoxFrame::GetBoxName(nsAutoString& aName)
1556 : {
1557 : GetFrameName(aName);
1558 : }
1559 : #endif
1560 :
1561 : #ifdef DEBUG
1562 : NS_IMETHODIMP
1563 0 : nsBoxFrame::GetFrameName(nsAString& aResult) const
1564 : {
1565 0 : return MakeFrameName(NS_LITERAL_STRING("Box"), aResult);
1566 : }
1567 : #endif
1568 :
1569 : nsIAtom*
1570 0 : nsBoxFrame::GetType() const
1571 : {
1572 0 : return nsGkAtoms::boxFrame;
1573 : }
1574 :
1575 : #ifdef DEBUG_LAYOUT
1576 : NS_IMETHODIMP
1577 : nsBoxFrame::GetDebug(bool& aDebug)
1578 : {
1579 : aDebug = (mState & NS_STATE_CURRENTLY_IN_DEBUG);
1580 : return NS_OK;
1581 : }
1582 : #endif
1583 :
1584 : // REVIEW: nsBoxFrame::GetFrameForPoint is a problem because of 'mousethrough'
1585 : // attribute support. Here's how it works:
1586 : // * For each child frame F, we determine the target frame T(F) by recursively
1587 : // invoking GetFrameForPoint on the child
1588 : // * Let F' be the last child frame such that T(F') doesn't have mousethrough.
1589 : // If F' exists, return T(F')
1590 : // * Otherwise let F'' be the first child frame such that T(F'') is non-null.
1591 : // If F'' exists, return T(F'')
1592 : // * Otherwise return this frame, if this frame contains the point
1593 : // * Otherwise return null
1594 : // It's not clear how this should work for more complex z-ordering situations.
1595 : // The basic principle seems to be that if a frame F has a descendant
1596 : // 'mousethrough' frame that includes the target position, then F
1597 : // will not receive events (unless it overrides GetFrameForPoint).
1598 : // A 'mousethrough' frame will only receive an event if, after applying that rule,
1599 : // all eligible frames are 'mousethrough'; the bottom-most inner-most 'mousethrough'
1600 : // frame is then chosen (the first eligible frame reached in a
1601 : // traversal of the frame tree --- pre/post is irrelevant since ancestors
1602 : // of the mousethrough frames can't be eligible).
1603 : // IMHO this is very bogus and adds a great deal of complexity for something
1604 : // that is very rarely used. So I'm redefining 'mousethrough' to the following:
1605 : // a frame with mousethrough is transparent to mouse events. This is compatible
1606 : // with the way 'mousethrough' is used in Seamonkey's navigator.xul and
1607 : // Firefox's browser.xul. The only other place it's used is in the 'expander'
1608 : // XBL binding, which in our tree is only used by Thunderbird SMIME Advanced
1609 : // Preferences, and I can't figure out what that does, so I'll have to test it.
1610 : // If it's broken I'll probably just change the binding to use it more sensibly.
1611 : // This new behaviour is implemented in nsDisplayList::HitTest.
1612 : // REVIEW: This debug-box stuff is annoying. I'm just going to put debug boxes
1613 : // in the outline layer and avoid GetDebugBoxAt.
1614 :
1615 : // REVIEW: GetCursor had debug-only event dumping code. I have replaced it
1616 : // with instrumentation in nsDisplayXULDebug.
1617 :
1618 : #ifdef DEBUG_LAYOUT
1619 : void
1620 : nsBoxFrame::DrawLine(nsRenderingContext& aRenderingContext, bool aHorizontal, nscoord x1, nscoord y1, nscoord x2, nscoord y2)
1621 : {
1622 : if (aHorizontal)
1623 : aRenderingContext.DrawLine(x1,y1,x2,y2);
1624 : else
1625 : aRenderingContext.DrawLine(y1,x1,y2,x2);
1626 : }
1627 :
1628 : void
1629 : nsBoxFrame::FillRect(nsRenderingContext& aRenderingContext, bool aHorizontal, nscoord x, nscoord y, nscoord width, nscoord height)
1630 : {
1631 : if (aHorizontal)
1632 : aRenderingContext.FillRect(x,y,width,height);
1633 : else
1634 : aRenderingContext.FillRect(y,x,height,width);
1635 : }
1636 :
1637 : void
1638 : nsBoxFrame::DrawSpacer(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, bool aHorizontal, PRInt32 flex, nscoord x, nscoord y, nscoord size, nscoord spacerSize)
1639 : {
1640 : nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
1641 :
1642 : // if we do draw the coils
1643 : int distance = 0;
1644 : int center = 0;
1645 : int offset = 0;
1646 : int coilSize = COIL_SIZE*onePixel;
1647 : int halfSpacer = spacerSize/2;
1648 :
1649 : distance = size;
1650 : center = y + halfSpacer;
1651 : offset = x;
1652 :
1653 : int coils = distance/coilSize;
1654 :
1655 : int halfCoilSize = coilSize/2;
1656 :
1657 : if (flex == 0) {
1658 : DrawLine(aRenderingContext, aHorizontal, x,y + spacerSize/2, x + size, y + spacerSize/2);
1659 : } else {
1660 : for (int i=0; i < coils; i++)
1661 : {
1662 : DrawLine(aRenderingContext, aHorizontal, offset, center+halfSpacer, offset+halfCoilSize, center-halfSpacer);
1663 : DrawLine(aRenderingContext, aHorizontal, offset+halfCoilSize, center-halfSpacer, offset+coilSize, center+halfSpacer);
1664 :
1665 : offset += coilSize;
1666 : }
1667 : }
1668 :
1669 : FillRect(aRenderingContext, aHorizontal, x + size - spacerSize/2, y, spacerSize/2, spacerSize);
1670 : FillRect(aRenderingContext, aHorizontal, x, y, spacerSize/2, spacerSize);
1671 :
1672 : //DrawKnob(aPresContext, aRenderingContext, x + size - spacerSize, y, spacerSize);
1673 : }
1674 :
1675 : void
1676 : nsBoxFrame::GetDebugBorder(nsMargin& aInset)
1677 : {
1678 : aInset.SizeTo(2,2,2,2);
1679 :
1680 : if (IsHorizontal())
1681 : aInset.top = 10;
1682 : else
1683 : aInset.left = 10;
1684 : }
1685 :
1686 : void
1687 : nsBoxFrame::GetDebugMargin(nsMargin& aInset)
1688 : {
1689 : aInset.SizeTo(2,2,2,2);
1690 : }
1691 :
1692 : void
1693 : nsBoxFrame::GetDebugPadding(nsMargin& aPadding)
1694 : {
1695 : aPadding.SizeTo(2,2,2,2);
1696 : }
1697 :
1698 : void
1699 : nsBoxFrame::PixelMarginToTwips(nsPresContext* aPresContext, nsMargin& aMarginPixels)
1700 : {
1701 : nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
1702 : aMarginPixels.left *= onePixel;
1703 : aMarginPixels.right *= onePixel;
1704 : aMarginPixels.top *= onePixel;
1705 : aMarginPixels.bottom *= onePixel;
1706 : }
1707 :
1708 : void
1709 : nsBoxFrame::GetValue(nsPresContext* aPresContext, const nsSize& a, const nsSize& b, char* ch)
1710 : {
1711 : float p2t = aPresContext->ScaledPixelsToTwips();
1712 :
1713 : char width[100];
1714 : char height[100];
1715 :
1716 : if (a.width == NS_INTRINSICSIZE)
1717 : sprintf(width,"%s","INF");
1718 : else
1719 : sprintf(width,"%d", nscoord(a.width/*/p2t*/));
1720 :
1721 : if (a.height == NS_INTRINSICSIZE)
1722 : sprintf(height,"%s","INF");
1723 : else
1724 : sprintf(height,"%d", nscoord(a.height/*/p2t*/));
1725 :
1726 :
1727 : sprintf(ch, "(%s%s, %s%s)", width, (b.width != NS_INTRINSICSIZE ? "[SET]" : ""),
1728 : height, (b.height != NS_INTRINSICSIZE ? "[SET]" : ""));
1729 :
1730 : }
1731 :
1732 : void
1733 : nsBoxFrame::GetValue(nsPresContext* aPresContext, PRInt32 a, PRInt32 b, char* ch)
1734 : {
1735 : if (a == NS_INTRINSICSIZE)
1736 : sprintf(ch, "%d[SET]", b);
1737 : else
1738 : sprintf(ch, "%d", a);
1739 : }
1740 :
1741 : nsresult
1742 : nsBoxFrame::DisplayDebugInfoFor(nsIBox* aBox,
1743 : nsPoint& aPoint)
1744 : {
1745 : nsBoxLayoutState state(GetPresContext());
1746 :
1747 : nscoord x = aPoint.x;
1748 : nscoord y = aPoint.y;
1749 :
1750 : // get the area inside our border but not our debug margins.
1751 : nsRect insideBorder(aBox->mRect);
1752 : insideBorder.MoveTo(0,0):
1753 : nsMargin border(0,0,0,0);
1754 : aBox->GetBorderAndPadding(border);
1755 : insideBorder.Deflate(border);
1756 :
1757 : bool isHorizontal = IsHorizontal();
1758 :
1759 : if (!insideBorder.Contains(nsPoint(x,y)))
1760 : return NS_ERROR_FAILURE;
1761 :
1762 : //printf("%%%%%% inside box %%%%%%%\n");
1763 :
1764 : int count = 0;
1765 : nsIBox* child = aBox->GetChildBox();
1766 :
1767 : nsMargin m;
1768 : nsMargin m2;
1769 : GetDebugBorder(m);
1770 : PixelMarginToTwips(aPresContext, m);
1771 :
1772 : GetDebugMargin(m2);
1773 : PixelMarginToTwips(aPresContext, m2);
1774 :
1775 : m += m2;
1776 :
1777 : if ((isHorizontal && y < insideBorder.y + m.top) ||
1778 : (!isHorizontal && x < insideBorder.x + m.left)) {
1779 : //printf("**** inside debug border *******\n");
1780 : while (child)
1781 : {
1782 : const nsRect& r = child->mRect;
1783 :
1784 : // if we are not in the child. But in the spacer above the child.
1785 : if ((isHorizontal && x >= r.x && x < r.x + r.width) ||
1786 : (!isHorizontal && y >= r.y && y < r.y + r.height)) {
1787 : aCursor = NS_STYLE_CURSOR_POINTER;
1788 : // found it but we already showed it.
1789 : if (mDebugChild == child)
1790 : return NS_OK;
1791 :
1792 : if (aBox->GetContent()) {
1793 : printf("---------------\n");
1794 : DumpBox(stdout);
1795 : printf("\n");
1796 : }
1797 :
1798 : if (child->GetContent()) {
1799 : printf("child #%d: ", count);
1800 : child->DumpBox(stdout);
1801 : printf("\n");
1802 : }
1803 :
1804 : mDebugChild = child;
1805 :
1806 : nsSize prefSizeCSS(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1807 : nsSize minSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1808 : nsSize maxSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1809 : nscoord flexCSS = NS_INTRINSICSIZE;
1810 :
1811 : bool widthSet, heightSet;
1812 : nsIBox::AddCSSPrefSize(child, prefSizeCSS, widthSet, heightSet);
1813 : nsIBox::AddCSSMinSize (state, child, minSizeCSS, widthSet, heightSet);
1814 : nsIBox::AddCSSMaxSize (child, maxSizeCSS, widthSet, heightSet);
1815 : nsIBox::AddCSSFlex (state, child, flexCSS);
1816 :
1817 : nsSize prefSize = child->GetPrefSize(state);
1818 : nsSize minSize = child->GetMinSize(state);
1819 : nsSize maxSize = child->GetMaxSize(state);
1820 : nscoord flexSize = child->GetFlex(state);
1821 : nscoord ascentSize = child->GetBoxAscent(state);
1822 :
1823 : char min[100];
1824 : char pref[100];
1825 : char max[100];
1826 : char calc[100];
1827 : char flex[100];
1828 : char ascent[100];
1829 :
1830 : nsSize actualSize;
1831 : GetFrameSizeWithMargin(child, actualSize);
1832 : nsSize actualSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1833 :
1834 : GetValue(aPresContext, minSize, minSizeCSS, min);
1835 : GetValue(aPresContext, prefSize, prefSizeCSS, pref);
1836 : GetValue(aPresContext, maxSize, maxSizeCSS, max);
1837 : GetValue(aPresContext, actualSize, actualSizeCSS, calc);
1838 : GetValue(aPresContext, flexSize, flexCSS, flex);
1839 : GetValue(aPresContext, ascentSize, NS_INTRINSICSIZE, ascent);
1840 :
1841 :
1842 : printf("min%s, pref%s, max%s, actual%s, flex=%s, ascent=%s\n\n",
1843 : min,
1844 : pref,
1845 : max,
1846 : calc,
1847 : flex,
1848 : ascent
1849 : );
1850 :
1851 : return NS_OK;
1852 : }
1853 :
1854 : child = child->GetNextBox();
1855 : count++;
1856 : }
1857 : } else {
1858 : }
1859 :
1860 : mDebugChild = nsnull;
1861 :
1862 : return NS_OK;
1863 : }
1864 :
1865 : void
1866 : nsBoxFrame::SetDebugOnChildList(nsBoxLayoutState& aState, nsIBox* aChild, bool aDebug)
1867 : {
1868 : nsIBox* child = GetChildBox();
1869 : while (child)
1870 : {
1871 : child->SetDebug(aState, aDebug);
1872 : child = child->GetNextBox();
1873 : }
1874 : }
1875 :
1876 : nsresult
1877 : nsBoxFrame::GetFrameSizeWithMargin(nsIBox* aBox, nsSize& aSize)
1878 : {
1879 : nsRect rect(aBox->GetRect());
1880 : nsMargin margin(0,0,0,0);
1881 : aBox->GetMargin(margin);
1882 : rect.Inflate(margin);
1883 : aSize.width = rect.width;
1884 : aSize.height = rect.height;
1885 : return NS_OK;
1886 : }
1887 : #endif
1888 :
1889 : // If you make changes to this function, check its counterparts
1890 : // in nsTextBoxFrame and nsXULLabelFrame
1891 : nsresult
1892 0 : nsBoxFrame::RegUnregAccessKey(bool aDoReg)
1893 : {
1894 : // if we have no content, we can't do anything
1895 0 : if (!mContent)
1896 0 : return NS_ERROR_FAILURE;
1897 :
1898 : // find out what type of element this is
1899 0 : nsIAtom *atom = mContent->Tag();
1900 :
1901 : // only support accesskeys for the following elements
1902 0 : if (atom != nsGkAtoms::button &&
1903 : atom != nsGkAtoms::toolbarbutton &&
1904 : atom != nsGkAtoms::checkbox &&
1905 : atom != nsGkAtoms::textbox &&
1906 : atom != nsGkAtoms::tab &&
1907 : atom != nsGkAtoms::radio) {
1908 0 : return NS_OK;
1909 : }
1910 :
1911 0 : nsAutoString accessKey;
1912 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
1913 :
1914 0 : if (accessKey.IsEmpty())
1915 0 : return NS_OK;
1916 :
1917 : // With a valid PresContext we can get the ESM
1918 : // and register the access key
1919 0 : nsEventStateManager *esm = PresContext()->EventStateManager();
1920 :
1921 0 : PRUint32 key = accessKey.First();
1922 0 : if (aDoReg)
1923 0 : esm->RegisterAccessKey(mContent, key);
1924 : else
1925 0 : esm->UnregisterAccessKey(mContent, key);
1926 :
1927 0 : return NS_OK;
1928 : }
1929 :
1930 : bool
1931 0 : nsBoxFrame::SupportsOrdinalsInChildren()
1932 : {
1933 0 : return true;
1934 : }
1935 :
1936 : static nsIFrame*
1937 0 : SortedMerge(nsBoxLayoutState& aState, nsIFrame *aLeft, nsIFrame *aRight)
1938 : {
1939 0 : NS_PRECONDITION(aLeft && aRight, "SortedMerge must have non-empty lists");
1940 :
1941 : nsIFrame *result;
1942 : // Unroll first iteration to avoid null-check 'result' inside the loop.
1943 0 : if (aLeft->GetOrdinal(aState) <= aRight->GetOrdinal(aState)) {
1944 0 : result = aLeft;
1945 0 : aLeft = aLeft->GetNextSibling();
1946 0 : if (!aLeft) {
1947 0 : result->SetNextSibling(aRight);
1948 0 : return result;
1949 : }
1950 : }
1951 : else {
1952 0 : result = aRight;
1953 0 : aRight = aRight->GetNextSibling();
1954 0 : if (!aRight) {
1955 0 : result->SetNextSibling(aLeft);
1956 0 : return result;
1957 : }
1958 : }
1959 :
1960 0 : nsIFrame *last = result;
1961 0 : for (;;) {
1962 0 : if (aLeft->GetOrdinal(aState) <= aRight->GetOrdinal(aState)) {
1963 0 : last->SetNextSibling(aLeft);
1964 0 : last = aLeft;
1965 0 : aLeft = aLeft->GetNextSibling();
1966 0 : if (!aLeft) {
1967 0 : last->SetNextSibling(aRight);
1968 0 : return result;
1969 : }
1970 : }
1971 : else {
1972 0 : last->SetNextSibling(aRight);
1973 0 : last = aRight;
1974 0 : aRight = aRight->GetNextSibling();
1975 0 : if (!aRight) {
1976 0 : last->SetNextSibling(aLeft);
1977 0 : return result;
1978 : }
1979 : }
1980 : }
1981 : }
1982 :
1983 : static nsIFrame*
1984 0 : MergeSort(nsBoxLayoutState& aState, nsIFrame *aSource)
1985 : {
1986 0 : NS_PRECONDITION(aSource, "MergeSort null arg");
1987 :
1988 0 : nsIFrame *sorted[32] = { nsnull };
1989 0 : nsIFrame **fill = &sorted[0];
1990 : nsIFrame **left;
1991 0 : nsIFrame *rest = aSource;
1992 :
1993 0 : do {
1994 0 : nsIFrame *current = rest;
1995 0 : rest = rest->GetNextSibling();
1996 0 : current->SetNextSibling(nsnull);
1997 :
1998 : // Merge it with sorted[0] if present; then merge the result with sorted[1] etc.
1999 : // sorted[0] is a list of length 1 (or nsnull).
2000 : // sorted[1] is a list of length 2 (or nsnull).
2001 : // sorted[2] is a list of length 4 (or nsnull). etc.
2002 0 : for (left = &sorted[0]; left != fill && *left; ++left) {
2003 0 : current = SortedMerge(aState, *left, current);
2004 0 : *left = nsnull;
2005 : }
2006 :
2007 : // Fill the empty slot that we couldn't merge with the last result.
2008 0 : *left = current;
2009 :
2010 0 : if (left == fill)
2011 0 : ++fill;
2012 : } while (rest);
2013 :
2014 : // Collect and merge the results.
2015 0 : nsIFrame *result = nsnull;
2016 0 : for (left = &sorted[0]; left != fill; ++left) {
2017 0 : if (*left) {
2018 0 : result = result ? SortedMerge(aState, *left, result) : *left;
2019 : }
2020 : }
2021 0 : return result;
2022 : }
2023 :
2024 : void
2025 0 : nsBoxFrame::CheckBoxOrder(nsBoxLayoutState& aState)
2026 : {
2027 0 : nsIFrame *child = mFrames.FirstChild();
2028 0 : if (!child)
2029 0 : return;
2030 :
2031 0 : if (!SupportsOrdinalsInChildren())
2032 0 : return;
2033 :
2034 : // Run through our list of children and check whether we
2035 : // need to sort them.
2036 0 : PRUint32 maxOrdinal = child->GetOrdinal(aState);
2037 0 : child = child->GetNextSibling();
2038 0 : for ( ; child; child = child->GetNextSibling()) {
2039 0 : PRUint32 ordinal = child->GetOrdinal(aState);
2040 0 : if (ordinal < maxOrdinal)
2041 0 : break;
2042 0 : maxOrdinal = ordinal;
2043 : }
2044 :
2045 0 : if (!child)
2046 0 : return;
2047 :
2048 0 : nsIFrame* head = MergeSort(aState, mFrames.FirstChild());
2049 0 : mFrames = nsFrameList(head, nsLayoutUtils::GetLastSibling(head));
2050 : }
2051 :
2052 : nsresult
2053 0 : nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIBox* aBox, const nsRect& aRect)
2054 : {
2055 : // get the current rect
2056 0 : nsRect oldRect(aBox->GetRect());
2057 0 : aBox->SetBounds(aState, aRect);
2058 :
2059 0 : bool layout = NS_SUBTREE_DIRTY(aBox);
2060 :
2061 0 : if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height)) {
2062 0 : return aBox->Layout(aState);
2063 : }
2064 :
2065 0 : return NS_OK;
2066 : }
2067 :
2068 : NS_IMETHODIMP
2069 0 : nsBoxFrame::RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIBox* aChild)
2070 : {
2071 0 : if (!SupportsOrdinalsInChildren())
2072 0 : return NS_OK;
2073 :
2074 0 : PRUint32 ord = aChild->GetOrdinal(aState);
2075 :
2076 0 : nsIFrame* child = mFrames.FirstChild();
2077 0 : nsIFrame* newPrevSib = nsnull;
2078 :
2079 0 : while (child) {
2080 0 : if (ord < child->GetOrdinal(aState)) {
2081 0 : break;
2082 : }
2083 :
2084 0 : if (child != aChild) {
2085 0 : newPrevSib = child;
2086 : }
2087 :
2088 0 : child = child->GetNextBox();
2089 : }
2090 :
2091 0 : if (aChild->GetPrevSibling() == newPrevSib) {
2092 : // This box is not moving.
2093 0 : return NS_OK;
2094 : }
2095 :
2096 : // Take |aChild| out of its old position in the child list.
2097 0 : mFrames.RemoveFrame(aChild);
2098 :
2099 : // Insert it after |newPrevSib| or at the start if it's null.
2100 0 : mFrames.InsertFrame(nsnull, newPrevSib, aChild);
2101 :
2102 0 : return NS_OK;
2103 : }
2104 :
2105 : /**
2106 : * This wrapper class lets us redirect mouse hits from descendant frames
2107 : * of a menu to the menu itself, if they didn't specify 'allowevents'.
2108 : *
2109 : * The wrapper simply turns a hit on a descendant element
2110 : * into a hit on the menu itself, unless there is an element between the target
2111 : * and the menu with the "allowevents" attribute.
2112 : *
2113 : * This is used by nsMenuFrame and nsTreeColFrame.
2114 : *
2115 : * Note that turning a hit on a descendant element into nsnull, so events
2116 : * could fall through to the menu background, might be an appealing simplification
2117 : * but it would mean slightly strange behaviour in some cases, because grabber
2118 : * wrappers can be created for many individual lists and items, so the exact
2119 : * fallthrough behaviour would be complex. E.g. an element with "allowevents"
2120 : * on top of the Content() list could receive the event even if it was covered
2121 : * by a PositionedDescenants() element without "allowevents". It is best to
2122 : * never convert a non-null hit into null.
2123 : */
2124 : // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
2125 : // I've made 'allowevents' affect child elements because that seems the only
2126 : // reasonable thing to do.
2127 0 : class nsDisplayXULEventRedirector : public nsDisplayWrapList {
2128 : public:
2129 0 : nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
2130 : nsIFrame* aFrame, nsDisplayItem* aItem,
2131 : nsIFrame* aTargetFrame)
2132 0 : : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {}
2133 0 : nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
2134 : nsIFrame* aFrame, nsDisplayList* aList,
2135 : nsIFrame* aTargetFrame)
2136 0 : : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {}
2137 : virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2138 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
2139 0 : NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
2140 : private:
2141 : nsIFrame* mTargetFrame;
2142 : };
2143 :
2144 0 : void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
2145 : const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
2146 : {
2147 0 : nsTArray<nsIFrame*> outFrames;
2148 0 : mList.HitTest(aBuilder, aRect, aState, &outFrames);
2149 :
2150 0 : bool topMostAdded = false;
2151 0 : PRUint32 localLength = outFrames.Length();
2152 :
2153 0 : for (PRUint32 i = 0; i < localLength; i++) {
2154 :
2155 0 : for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
2156 0 : content && content != mTargetFrame->GetContent();
2157 0 : content = content->GetParent()) {
2158 0 : if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
2159 0 : nsGkAtoms::_true, eCaseMatters)) {
2160 : // Events are allowed on 'frame', so let it go.
2161 0 : aOutFrames->AppendElement(outFrames.ElementAt(i));
2162 0 : topMostAdded = true;
2163 : }
2164 : }
2165 :
2166 : // If there was no hit on the topmost frame or its ancestors,
2167 : // add the target frame itself as the first candidate (see bug 562554).
2168 0 : if (!topMostAdded) {
2169 0 : topMostAdded = true;
2170 0 : aOutFrames->AppendElement(mTargetFrame);
2171 : }
2172 : }
2173 0 : }
2174 :
2175 : class nsXULEventRedirectorWrapper : public nsDisplayWrapper
2176 : {
2177 : public:
2178 0 : nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
2179 0 : : mTargetFrame(aTargetFrame) {}
2180 0 : virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
2181 : nsIFrame* aFrame, nsDisplayList* aList) {
2182 : return new (aBuilder)
2183 0 : nsDisplayXULEventRedirector(aBuilder, aFrame, aList, mTargetFrame);
2184 : }
2185 0 : virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
2186 : nsDisplayItem* aItem) {
2187 : return new (aBuilder)
2188 : nsDisplayXULEventRedirector(aBuilder, aItem->GetUnderlyingFrame(), aItem,
2189 0 : mTargetFrame);
2190 : }
2191 : private:
2192 : nsIFrame* mTargetFrame;
2193 : };
2194 :
2195 : nsresult
2196 0 : nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder* aBuilder,
2197 : const nsDisplayListSet& aIn,
2198 : const nsDisplayListSet& aOut)
2199 : {
2200 0 : nsXULEventRedirectorWrapper wrapper(this);
2201 0 : return wrapper.WrapLists(aBuilder, this, aIn, aOut);
2202 : }
|