|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 #include "nsTableFrame.h" |
|
6 #include "nsTableColFrame.h" |
|
7 #include "nsTableCellFrame.h" |
|
8 #include "nsTableRowFrame.h" |
|
9 #include "nsTableRowGroupFrame.h" |
|
10 #include "nsTablePainter.h" |
|
11 #include "nsStyleContext.h" |
|
12 #include "nsStyleConsts.h" |
|
13 #include "nsPresContext.h" |
|
14 #include "nsRenderingContext.h" |
|
15 #include "nsCSSRendering.h" |
|
16 #include "nsIContent.h" |
|
17 #include "nsGenericHTMLElement.h" |
|
18 #include "nsAttrValueInlines.h" |
|
19 #include "nsHTMLParts.h" |
|
20 #include "nsGkAtoms.h" |
|
21 #include "nsIPresShell.h" |
|
22 #include "nsCOMPtr.h" |
|
23 #include "nsIServiceManager.h" |
|
24 #include "nsIDOMNode.h" |
|
25 #include "nsNameSpaceManager.h" |
|
26 #include "nsDisplayList.h" |
|
27 #include "nsLayoutUtils.h" |
|
28 #include "nsTextFrame.h" |
|
29 #include "FrameLayerBuilder.h" |
|
30 #include <algorithm> |
|
31 |
|
32 //TABLECELL SELECTION |
|
33 #include "nsFrameSelection.h" |
|
34 #include "mozilla/LookAndFeel.h" |
|
35 |
|
36 using namespace mozilla; |
|
37 |
|
38 |
|
39 nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) : |
|
40 nsContainerFrame(aContext) |
|
41 { |
|
42 mColIndex = 0; |
|
43 mPriorAvailWidth = 0; |
|
44 |
|
45 SetContentEmpty(false); |
|
46 SetHasPctOverHeight(false); |
|
47 } |
|
48 |
|
49 nsTableCellFrame::~nsTableCellFrame() |
|
50 { |
|
51 } |
|
52 |
|
53 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame) |
|
54 |
|
55 nsTableCellFrame* |
|
56 nsTableCellFrame::GetNextCell() const |
|
57 { |
|
58 nsIFrame* childFrame = GetNextSibling(); |
|
59 while (childFrame) { |
|
60 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); |
|
61 if (cellFrame) { |
|
62 return cellFrame; |
|
63 } |
|
64 childFrame = childFrame->GetNextSibling(); |
|
65 } |
|
66 return nullptr; |
|
67 } |
|
68 |
|
69 void |
|
70 nsTableCellFrame::Init(nsIContent* aContent, |
|
71 nsIFrame* aParent, |
|
72 nsIFrame* aPrevInFlow) |
|
73 { |
|
74 // Let the base class do its initialization |
|
75 nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
|
76 |
|
77 if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) { |
|
78 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); |
|
79 } |
|
80 |
|
81 if (aPrevInFlow) { |
|
82 // Set the column index |
|
83 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow; |
|
84 int32_t colIndex; |
|
85 cellFrame->GetColIndex(colIndex); |
|
86 SetColIndex(colIndex); |
|
87 } |
|
88 } |
|
89 |
|
90 void |
|
91 nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
92 { |
|
93 if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { |
|
94 nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot); |
|
95 } |
|
96 |
|
97 nsContainerFrame::DestroyFrom(aDestructRoot); |
|
98 } |
|
99 |
|
100 // nsIPercentHeightObserver methods |
|
101 |
|
102 void |
|
103 nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState) |
|
104 { |
|
105 // nsHTMLReflowState ensures the mCBReflowState of blocks inside a |
|
106 // cell is the cell frame, not the inner-cell block, and that the |
|
107 // containing block of an inner table is the containing block of its |
|
108 // outer table. |
|
109 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of |
|
110 // these tests are probably unnecessary. |
|
111 |
|
112 // Maybe the cell reflow state; we sure if we're inside the |if|. |
|
113 const nsHTMLReflowState *cellRS = aReflowState.mCBReflowState; |
|
114 |
|
115 if (cellRS && cellRS->frame == this && |
|
116 (cellRS->ComputedHeight() == NS_UNCONSTRAINEDSIZE || |
|
117 cellRS->ComputedHeight() == 0)) { // XXXldb Why 0? |
|
118 // This is a percentage height on a frame whose percentage heights |
|
119 // are based on the height of the cell, since its containing block |
|
120 // is the inner cell frame. |
|
121 |
|
122 // We'll only honor the percent height if sibling-cells/ancestors |
|
123 // have specified/pct height. (Also, siblings only count for this if |
|
124 // both this cell and the sibling cell span exactly 1 row.) |
|
125 |
|
126 if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) || |
|
127 (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 && |
|
128 (cellRS->parentReflowState->frame->GetStateBits() & |
|
129 NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) { |
|
130 |
|
131 for (const nsHTMLReflowState *rs = aReflowState.parentReflowState; |
|
132 rs != cellRS; |
|
133 rs = rs->parentReflowState) { |
|
134 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
|
135 } |
|
136 |
|
137 nsTableFrame::RequestSpecialHeightReflow(*cellRS); |
|
138 } |
|
139 } |
|
140 } |
|
141 |
|
142 // The cell needs to observe its block and things inside its block but nothing below that |
|
143 bool |
|
144 nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState) |
|
145 { |
|
146 const nsHTMLReflowState *rs = aReflowState.parentReflowState; |
|
147 if (!rs) |
|
148 return false; |
|
149 if (rs->frame == this) { |
|
150 // We always observe the child block. It will never send any |
|
151 // notifications, but we need this so that the observer gets |
|
152 // propagated to its kids. |
|
153 return true; |
|
154 } |
|
155 rs = rs->parentReflowState; |
|
156 if (!rs) { |
|
157 return false; |
|
158 } |
|
159 |
|
160 // We always need to let the percent height observer be propagated |
|
161 // from an outer table frame to an inner table frame. |
|
162 nsIAtom *fType = aReflowState.frame->GetType(); |
|
163 if (fType == nsGkAtoms::tableFrame) { |
|
164 return true; |
|
165 } |
|
166 |
|
167 // We need the observer to be propagated to all children of the cell |
|
168 // (i.e., children of the child block) in quirks mode, but only to |
|
169 // tables in standards mode. |
|
170 return rs->frame == this && |
|
171 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks || |
|
172 fType == nsGkAtoms::tableOuterFrame); |
|
173 } |
|
174 |
|
175 nsresult |
|
176 nsTableCellFrame::GetRowIndex(int32_t &aRowIndex) const |
|
177 { |
|
178 nsresult result; |
|
179 nsTableRowFrame* row = static_cast<nsTableRowFrame*>(GetParent()); |
|
180 if (row) { |
|
181 aRowIndex = row->GetRowIndex(); |
|
182 result = NS_OK; |
|
183 } |
|
184 else { |
|
185 aRowIndex = 0; |
|
186 result = NS_ERROR_NOT_INITIALIZED; |
|
187 } |
|
188 return result; |
|
189 } |
|
190 |
|
191 nsresult |
|
192 nsTableCellFrame::GetColIndex(int32_t &aColIndex) const |
|
193 { |
|
194 if (GetPrevInFlow()) { |
|
195 return static_cast<nsTableCellFrame*>(FirstInFlow())->GetColIndex(aColIndex); |
|
196 } |
|
197 else { |
|
198 aColIndex = mColIndex; |
|
199 return NS_OK; |
|
200 } |
|
201 } |
|
202 |
|
203 nsresult |
|
204 nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID, |
|
205 nsIAtom* aAttribute, |
|
206 int32_t aModType) |
|
207 { |
|
208 // We need to recalculate in this case because of the nowrap quirk in |
|
209 // BasicTableLayoutStrategy |
|
210 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap && |
|
211 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) { |
|
212 PresContext()->PresShell()-> |
|
213 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
|
214 } |
|
215 // let the table frame decide what to do |
|
216 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
|
217 tableFrame->AttributeChangedFor(this, mContent, aAttribute); |
|
218 return NS_OK; |
|
219 } |
|
220 |
|
221 /* virtual */ void |
|
222 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
|
223 { |
|
224 nsContainerFrame::DidSetStyleContext(aOldStyleContext); |
|
225 |
|
226 if (!aOldStyleContext) //avoid this on init |
|
227 return; |
|
228 |
|
229 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
|
230 if (tableFrame->IsBorderCollapse() && |
|
231 tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) { |
|
232 int32_t colIndex, rowIndex; |
|
233 GetColIndex(colIndex); |
|
234 GetRowIndex(rowIndex); |
|
235 // row span needs to be clamped as we do not create rows in the cellmap |
|
236 // which do not have cells originating in them |
|
237 nsIntRect damageArea(colIndex, rowIndex, GetColSpan(), |
|
238 std::min(GetRowSpan(), tableFrame->GetRowCount() - rowIndex)); |
|
239 tableFrame->AddBCDamageArea(damageArea); |
|
240 } |
|
241 } |
|
242 |
|
243 |
|
244 nsresult |
|
245 nsTableCellFrame::AppendFrames(ChildListID aListID, |
|
246 nsFrameList& aFrameList) |
|
247 { |
|
248 NS_PRECONDITION(false, "unsupported operation"); |
|
249 return NS_ERROR_NOT_IMPLEMENTED; |
|
250 } |
|
251 |
|
252 nsresult |
|
253 nsTableCellFrame::InsertFrames(ChildListID aListID, |
|
254 nsIFrame* aPrevFrame, |
|
255 nsFrameList& aFrameList) |
|
256 { |
|
257 NS_PRECONDITION(false, "unsupported operation"); |
|
258 return NS_ERROR_NOT_IMPLEMENTED; |
|
259 } |
|
260 |
|
261 nsresult |
|
262 nsTableCellFrame::RemoveFrame(ChildListID aListID, |
|
263 nsIFrame* aOldFrame) |
|
264 { |
|
265 NS_PRECONDITION(false, "unsupported operation"); |
|
266 return NS_ERROR_NOT_IMPLEMENTED; |
|
267 } |
|
268 |
|
269 void nsTableCellFrame::SetColIndex(int32_t aColIndex) |
|
270 { |
|
271 mColIndex = aColIndex; |
|
272 } |
|
273 |
|
274 /* virtual */ nsMargin |
|
275 nsTableCellFrame::GetUsedMargin() const |
|
276 { |
|
277 return nsMargin(0,0,0,0); |
|
278 } |
|
279 |
|
280 //ASSURE DIFFERENT COLORS for selection |
|
281 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) |
|
282 { |
|
283 if (colorA == colorB) |
|
284 { |
|
285 nscolor res; |
|
286 res = NS_RGB(NS_GET_R(colorA) ^ 0xff, |
|
287 NS_GET_G(colorA) ^ 0xff, |
|
288 NS_GET_B(colorA) ^ 0xff); |
|
289 return res; |
|
290 } |
|
291 return colorA; |
|
292 } |
|
293 |
|
294 void |
|
295 nsTableCellFrame::DecorateForSelection(nsRenderingContext& aRenderingContext, |
|
296 nsPoint aPt) |
|
297 { |
|
298 NS_ASSERTION(IsSelected(), "Should only be called for selected cells"); |
|
299 int16_t displaySelection; |
|
300 nsPresContext* presContext = PresContext(); |
|
301 displaySelection = DisplaySelection(presContext); |
|
302 if (displaySelection) { |
|
303 nsRefPtr<nsFrameSelection> frameSelection = |
|
304 presContext->PresShell()->FrameSelection(); |
|
305 |
|
306 if (frameSelection->GetTableCellSelection()) { |
|
307 nscolor bordercolor; |
|
308 if (displaySelection == nsISelectionController::SELECTION_DISABLED) { |
|
309 bordercolor = NS_RGB(176,176,176);// disabled color |
|
310 } |
|
311 else { |
|
312 bordercolor = |
|
313 LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground); |
|
314 } |
|
315 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3); |
|
316 if ((mRect.width > threePx) && (mRect.height > threePx)) |
|
317 { |
|
318 //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor) |
|
319 bordercolor = EnsureDifferentColors(bordercolor, |
|
320 StyleBackground()->mBackgroundColor); |
|
321 nsRenderingContext::AutoPushTranslation |
|
322 translate(&aRenderingContext, aPt); |
|
323 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); |
|
324 |
|
325 aRenderingContext.SetColor(bordercolor); |
|
326 aRenderingContext.DrawLine(onePixel, 0, mRect.width, 0); |
|
327 aRenderingContext.DrawLine(0, onePixel, 0, mRect.height); |
|
328 aRenderingContext.DrawLine(onePixel, mRect.height, mRect.width, mRect.height); |
|
329 aRenderingContext.DrawLine(mRect.width, onePixel, mRect.width, mRect.height); |
|
330 //middle |
|
331 aRenderingContext.DrawRect(onePixel, onePixel, mRect.width-onePixel, |
|
332 mRect.height-onePixel); |
|
333 //shading |
|
334 aRenderingContext.DrawLine(2*onePixel, mRect.height-2*onePixel, |
|
335 mRect.width-onePixel, mRect.height- (2*onePixel)); |
|
336 aRenderingContext.DrawLine(mRect.width - (2*onePixel), 2*onePixel, |
|
337 mRect.width - (2*onePixel), mRect.height-onePixel); |
|
338 } |
|
339 } |
|
340 } |
|
341 } |
|
342 |
|
343 void |
|
344 nsTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext, |
|
345 const nsRect& aDirtyRect, |
|
346 nsPoint aPt, |
|
347 uint32_t aFlags) |
|
348 { |
|
349 nsRect rect(aPt, GetSize()); |
|
350 nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this, |
|
351 aDirtyRect, rect, aFlags); |
|
352 } |
|
353 |
|
354 // Called by nsTablePainter |
|
355 void |
|
356 nsTableCellFrame::PaintCellBackground(nsRenderingContext& aRenderingContext, |
|
357 const nsRect& aDirtyRect, nsPoint aPt, |
|
358 uint32_t aFlags) |
|
359 { |
|
360 if (!StyleVisibility()->IsVisible()) |
|
361 return; |
|
362 |
|
363 PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags); |
|
364 } |
|
365 |
|
366 nsresult |
|
367 nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame, |
|
368 nsDisplayListBuilder* aBuilder, |
|
369 const nsDisplayListSet& aLists) |
|
370 { |
|
371 const nsStyleBorder* borderStyle = StyleBorder(); |
|
372 if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder()) |
|
373 return NS_OK; |
|
374 |
|
375 if (!GetContentEmpty() || |
|
376 StyleTableBorder()->mEmptyCells == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) { |
|
377 aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
|
378 nsDisplayBorder(aBuilder, this)); |
|
379 } |
|
380 |
|
381 return NS_OK; |
|
382 } |
|
383 |
|
384 class nsDisplayTableCellBackground : public nsDisplayTableItem { |
|
385 public: |
|
386 nsDisplayTableCellBackground(nsDisplayListBuilder* aBuilder, |
|
387 nsTableCellFrame* aFrame) : |
|
388 nsDisplayTableItem(aBuilder, aFrame) { |
|
389 MOZ_COUNT_CTOR(nsDisplayTableCellBackground); |
|
390 } |
|
391 #ifdef NS_BUILD_REFCNT_LOGGING |
|
392 virtual ~nsDisplayTableCellBackground() { |
|
393 MOZ_COUNT_DTOR(nsDisplayTableCellBackground); |
|
394 } |
|
395 #endif |
|
396 |
|
397 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
|
398 HitTestState* aState, |
|
399 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE { |
|
400 aOutFrames->AppendElement(mFrame); |
|
401 } |
|
402 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
403 nsRenderingContext* aCtx) MOZ_OVERRIDE; |
|
404 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
|
405 bool* aSnap) MOZ_OVERRIDE; |
|
406 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
407 const nsDisplayItemGeometry* aGeometry, |
|
408 nsRegion *aInvalidRegion) MOZ_OVERRIDE; |
|
409 |
|
410 NS_DISPLAY_DECL_NAME("TableCellBackground", TYPE_TABLE_CELL_BACKGROUND) |
|
411 }; |
|
412 |
|
413 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder, |
|
414 nsRenderingContext* aCtx) |
|
415 { |
|
416 static_cast<nsTableCellFrame*>(mFrame)-> |
|
417 PaintBackground(*aCtx, mVisibleRect, ToReferenceFrame(), |
|
418 aBuilder->GetBackgroundPaintFlags()); |
|
419 } |
|
420 |
|
421 nsRect |
|
422 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder, |
|
423 bool* aSnap) |
|
424 { |
|
425 // revert from nsDisplayTableItem's implementation ... cell backgrounds |
|
426 // don't overflow the cell |
|
427 return nsDisplayItem::GetBounds(aBuilder, aSnap); |
|
428 } |
|
429 |
|
430 void |
|
431 nsDisplayTableCellBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
432 const nsDisplayItemGeometry* aGeometry, |
|
433 nsRegion *aInvalidRegion) |
|
434 { |
|
435 if (aBuilder->ShouldSyncDecodeImages()) { |
|
436 if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) { |
|
437 bool snap; |
|
438 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
|
439 } |
|
440 } |
|
441 |
|
442 nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
|
443 } |
|
444 |
|
445 void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey) |
|
446 { |
|
447 nsIFrame::InvalidateFrame(aDisplayItemKey); |
|
448 GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey); |
|
449 } |
|
450 |
|
451 void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) |
|
452 { |
|
453 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey); |
|
454 // If we have filters applied that would affects our bounds, then |
|
455 // we get an inactive layer created and this is computed |
|
456 // within FrameLayerBuilder |
|
457 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey); |
|
458 } |
|
459 |
|
460 static void |
|
461 PaintTableCellSelection(nsIFrame* aFrame, nsRenderingContext* aCtx, |
|
462 const nsRect& aRect, nsPoint aPt) |
|
463 { |
|
464 static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(*aCtx, aPt); |
|
465 } |
|
466 |
|
467 void |
|
468 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
469 const nsRect& aDirtyRect, |
|
470 const nsDisplayListSet& aLists) |
|
471 { |
|
472 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame"); |
|
473 if (IsVisibleInSelection(aBuilder)) { |
|
474 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
|
475 int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ? |
|
476 StyleTableBorder()->mEmptyCells |
|
477 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW; |
|
478 // take account of 'empty-cells' |
|
479 if (StyleVisibility()->IsVisible() && |
|
480 (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) { |
|
481 |
|
482 |
|
483 bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); |
|
484 if (!isRoot) { |
|
485 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); |
|
486 if (currentItem) { |
|
487 currentItem->UpdateForFrameBackground(this); |
|
488 } |
|
489 } |
|
490 |
|
491 // display outset box-shadows if we need to. |
|
492 const nsStyleBorder* borderStyle = StyleBorder(); |
|
493 bool hasBoxShadow = !!borderStyle->mBoxShadow; |
|
494 if (hasBoxShadow) { |
|
495 aLists.BorderBackground()->AppendNewToTop( |
|
496 new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); |
|
497 } |
|
498 |
|
499 // display background if we need to. |
|
500 if (aBuilder->IsForEventDelivery() || |
|
501 (((!tableFrame->IsBorderCollapse() || isRoot) && |
|
502 (!StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance)))) { |
|
503 // The cell background was not painted by the nsTablePainter, |
|
504 // so we need to do it. We have special background processing here |
|
505 // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline |
|
506 nsDisplayTableItem* item = |
|
507 new (aBuilder) nsDisplayTableCellBackground(aBuilder, this); |
|
508 aLists.BorderBackground()->AppendNewToTop(item); |
|
509 item->UpdateForFrameBackground(this); |
|
510 } |
|
511 |
|
512 // display inset box-shadows if we need to. |
|
513 if (hasBoxShadow) { |
|
514 aLists.BorderBackground()->AppendNewToTop( |
|
515 new (aBuilder) nsDisplayBoxShadowInner(aBuilder, this)); |
|
516 } |
|
517 |
|
518 // display borders if we need to |
|
519 ProcessBorders(tableFrame, aBuilder, aLists); |
|
520 |
|
521 // and display the selection border if we need to |
|
522 if (IsSelected()) { |
|
523 aLists.BorderBackground()->AppendNewToTop(new (aBuilder) |
|
524 nsDisplayGeneric(aBuilder, this, ::PaintTableCellSelection, |
|
525 "TableCellSelection", |
|
526 nsDisplayItem::TYPE_TABLE_CELL_SELECTION)); |
|
527 } |
|
528 } |
|
529 |
|
530 // the 'empty-cells' property has no effect on 'outline' |
|
531 DisplayOutline(aBuilder, aLists); |
|
532 } |
|
533 |
|
534 // Push a null 'current table item' so that descendant tables can't |
|
535 // accidentally mess with our table |
|
536 nsAutoPushCurrentTableItem pushTableItem; |
|
537 pushTableItem.Push(aBuilder, nullptr); |
|
538 |
|
539 nsIFrame* kid = mFrames.FirstChild(); |
|
540 NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child"); |
|
541 // The child's background will go in our BorderBackground() list. |
|
542 // This isn't a problem since it won't have a real background except for |
|
543 // event handling. We do not call BuildDisplayListForNonBlockChildren |
|
544 // because that/ would put the child's background in the Content() list |
|
545 // which isn't right (e.g., would end up on top of our child floats for |
|
546 // event handling). |
|
547 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
|
548 } |
|
549 |
|
550 int |
|
551 nsTableCellFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const |
|
552 { |
|
553 int skip = 0; |
|
554 if (nullptr != GetPrevInFlow()) { |
|
555 skip |= LOGICAL_SIDE_B_START; |
|
556 } |
|
557 if (nullptr != GetNextInFlow()) { |
|
558 skip |= LOGICAL_SIDE_B_END; |
|
559 } |
|
560 return skip; |
|
561 } |
|
562 |
|
563 /* virtual */ nsMargin |
|
564 nsTableCellFrame::GetBorderOverflow() |
|
565 { |
|
566 return nsMargin(0, 0, 0, 0); |
|
567 } |
|
568 |
|
569 // Align the cell's child frame within the cell |
|
570 |
|
571 void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent) |
|
572 { |
|
573 /* It's the 'border-collapse' on the table that matters */ |
|
574 nsMargin borderPadding = GetUsedBorderAndPadding(); |
|
575 |
|
576 nscoord topInset = borderPadding.top; |
|
577 nscoord bottomInset = borderPadding.bottom; |
|
578 |
|
579 uint8_t verticalAlignFlags = GetVerticalAlign(); |
|
580 |
|
581 nscoord height = mRect.height; |
|
582 nsIFrame* firstKid = mFrames.FirstChild(); |
|
583 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); |
|
584 nsRect kidRect = firstKid->GetRect(); |
|
585 nscoord childHeight = kidRect.height; |
|
586 |
|
587 // Vertically align the child |
|
588 nscoord kidYTop = 0; |
|
589 switch (verticalAlignFlags) |
|
590 { |
|
591 case NS_STYLE_VERTICAL_ALIGN_BASELINE: |
|
592 // Align the baselines of the child frame with the baselines of |
|
593 // other children in the same row which have 'vertical-align: baseline' |
|
594 kidYTop = topInset + aMaxAscent - GetCellBaseline(); |
|
595 break; |
|
596 |
|
597 case NS_STYLE_VERTICAL_ALIGN_TOP: |
|
598 // Align the top of the child frame with the top of the content area, |
|
599 kidYTop = topInset; |
|
600 break; |
|
601 |
|
602 case NS_STYLE_VERTICAL_ALIGN_BOTTOM: |
|
603 // Align the bottom of the child frame with the bottom of the content area, |
|
604 kidYTop = height - childHeight - bottomInset; |
|
605 break; |
|
606 |
|
607 default: |
|
608 case NS_STYLE_VERTICAL_ALIGN_MIDDLE: |
|
609 // Align the middle of the child frame with the middle of the content area, |
|
610 kidYTop = (height - childHeight - bottomInset + topInset) / 2; |
|
611 } |
|
612 // if the content is larger than the cell height align from top |
|
613 kidYTop = std::max(0, kidYTop); |
|
614 |
|
615 if (kidYTop != kidRect.y) { |
|
616 // Invalidate at the old position first |
|
617 firstKid->InvalidateFrameSubtree(); |
|
618 } |
|
619 |
|
620 firstKid->SetPosition(nsPoint(kidRect.x, kidYTop)); |
|
621 nsHTMLReflowMetrics desiredSize(GetWritingMode()); // ??? |
|
622 desiredSize.Width() = mRect.width; |
|
623 desiredSize.Height() = mRect.height; |
|
624 |
|
625 nsRect overflow(nsPoint(0,0), GetSize()); |
|
626 overflow.Inflate(GetBorderOverflow()); |
|
627 desiredSize.mOverflowAreas.SetAllTo(overflow); |
|
628 ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid); |
|
629 FinishAndStoreOverflow(&desiredSize); |
|
630 if (kidYTop != kidRect.y) { |
|
631 // Make sure any child views are correctly positioned. We know the inner table |
|
632 // cell won't have a view |
|
633 nsContainerFrame::PositionChildViews(firstKid); |
|
634 |
|
635 // Invalidate new overflow rect |
|
636 firstKid->InvalidateFrameSubtree(); |
|
637 } |
|
638 if (HasView()) { |
|
639 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, |
|
640 GetView(), |
|
641 desiredSize.VisualOverflow(), 0); |
|
642 } |
|
643 } |
|
644 |
|
645 bool |
|
646 nsTableCellFrame::UpdateOverflow() |
|
647 { |
|
648 nsRect bounds(nsPoint(0,0), GetSize()); |
|
649 bounds.Inflate(GetBorderOverflow()); |
|
650 nsOverflowAreas overflowAreas(bounds, bounds); |
|
651 |
|
652 nsLayoutUtils::UnionChildOverflow(this, overflowAreas); |
|
653 |
|
654 return FinishAndStoreOverflow(overflowAreas, GetSize()); |
|
655 } |
|
656 |
|
657 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom', |
|
658 // length, percentage, and calc() values to 'baseline'. |
|
659 uint8_t |
|
660 nsTableCellFrame::GetVerticalAlign() const |
|
661 { |
|
662 const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; |
|
663 if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { |
|
664 uint8_t value = verticalAlign.GetIntValue(); |
|
665 if (value == NS_STYLE_VERTICAL_ALIGN_TOP || |
|
666 value == NS_STYLE_VERTICAL_ALIGN_MIDDLE || |
|
667 value == NS_STYLE_VERTICAL_ALIGN_BOTTOM) { |
|
668 return value; |
|
669 } |
|
670 } |
|
671 return NS_STYLE_VERTICAL_ALIGN_BASELINE; |
|
672 } |
|
673 |
|
674 bool |
|
675 nsTableCellFrame::CellHasVisibleContent(nscoord height, |
|
676 nsTableFrame* tableFrame, |
|
677 nsIFrame* kidFrame) |
|
678 { |
|
679 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells |
|
680 if (height > 0) |
|
681 return true; |
|
682 if (tableFrame->IsBorderCollapse()) |
|
683 return true; |
|
684 nsIFrame* innerFrame = kidFrame->GetFirstPrincipalChild(); |
|
685 while(innerFrame) { |
|
686 nsIAtom* frameType = innerFrame->GetType(); |
|
687 if (nsGkAtoms::textFrame == frameType) { |
|
688 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame); |
|
689 if (textFrame->HasNoncollapsedCharacters()) |
|
690 return true; |
|
691 } |
|
692 else if (nsGkAtoms::placeholderFrame != frameType) { |
|
693 return true; |
|
694 } |
|
695 else { |
|
696 nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame); |
|
697 if (floatFrame) |
|
698 return true; |
|
699 } |
|
700 innerFrame = innerFrame->GetNextSibling(); |
|
701 } |
|
702 return false; |
|
703 } |
|
704 |
|
705 nscoord |
|
706 nsTableCellFrame::GetCellBaseline() const |
|
707 { |
|
708 // Ignore the position of the inner frame relative to the cell frame |
|
709 // since we want the position as though the inner were top-aligned. |
|
710 nsIFrame *inner = mFrames.FirstChild(); |
|
711 nscoord borderPadding = GetUsedBorderAndPadding().top; |
|
712 nscoord result; |
|
713 if (nsLayoutUtils::GetFirstLineBaseline(inner, &result)) |
|
714 return result + borderPadding; |
|
715 return inner->GetContentRect().YMost() - inner->GetPosition().y + |
|
716 borderPadding; |
|
717 } |
|
718 |
|
719 int32_t nsTableCellFrame::GetRowSpan() |
|
720 { |
|
721 int32_t rowSpan=1; |
|
722 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent); |
|
723 |
|
724 // Don't look at the content's rowspan if we're a pseudo cell |
|
725 if (hc && !StyleContext()->GetPseudo()) { |
|
726 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan); |
|
727 // Note that we don't need to check the tag name, because only table cells |
|
728 // and table headers parse the "rowspan" attribute into an integer. |
|
729 if (attr && attr->Type() == nsAttrValue::eInteger) { |
|
730 rowSpan = attr->GetIntegerValue(); |
|
731 } |
|
732 } |
|
733 return rowSpan; |
|
734 } |
|
735 |
|
736 int32_t nsTableCellFrame::GetColSpan() |
|
737 { |
|
738 int32_t colSpan=1; |
|
739 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent); |
|
740 |
|
741 // Don't look at the content's colspan if we're a pseudo cell |
|
742 if (hc && !StyleContext()->GetPseudo()) { |
|
743 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan); |
|
744 // Note that we don't need to check the tag name, because only table cells |
|
745 // and table headers parse the "colspan" attribute into an integer. |
|
746 if (attr && attr->Type() == nsAttrValue::eInteger) { |
|
747 colSpan = attr->GetIntegerValue(); |
|
748 } |
|
749 } |
|
750 return colSpan; |
|
751 } |
|
752 |
|
753 /* virtual */ nscoord |
|
754 nsTableCellFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
755 { |
|
756 nscoord result = 0; |
|
757 DISPLAY_MIN_WIDTH(this, result); |
|
758 |
|
759 nsIFrame *inner = mFrames.FirstChild(); |
|
760 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, |
|
761 nsLayoutUtils::MIN_WIDTH); |
|
762 return result; |
|
763 } |
|
764 |
|
765 /* virtual */ nscoord |
|
766 nsTableCellFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
767 { |
|
768 nscoord result = 0; |
|
769 DISPLAY_PREF_WIDTH(this, result); |
|
770 |
|
771 nsIFrame *inner = mFrames.FirstChild(); |
|
772 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, |
|
773 nsLayoutUtils::PREF_WIDTH); |
|
774 return result; |
|
775 } |
|
776 |
|
777 /* virtual */ nsIFrame::IntrinsicWidthOffsetData |
|
778 nsTableCellFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) |
|
779 { |
|
780 IntrinsicWidthOffsetData result = |
|
781 nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext); |
|
782 |
|
783 result.hMargin = 0; |
|
784 result.hPctMargin = 0; |
|
785 |
|
786 nsMargin border; |
|
787 GetBorderWidth(border); |
|
788 result.hBorder = border.LeftRight(); |
|
789 |
|
790 return result; |
|
791 } |
|
792 |
|
793 #ifdef DEBUG |
|
794 #define PROBABLY_TOO_LARGE 1000000 |
|
795 static |
|
796 void DebugCheckChildSize(nsIFrame* aChild, |
|
797 nsHTMLReflowMetrics& aMet, |
|
798 nsSize& aAvailSize) |
|
799 { |
|
800 if ((aMet.Width() < 0) || (aMet.Width() > PROBABLY_TOO_LARGE)) { |
|
801 printf("WARNING: cell content %p has large width %d \n", |
|
802 static_cast<void*>(aChild), int32_t(aMet.Width())); |
|
803 } |
|
804 } |
|
805 #endif |
|
806 |
|
807 // the computed height for the cell, which descendants use for percent height calculations |
|
808 // it is the height (minus border, padding) of the cell's first in flow during its final |
|
809 // reflow without an unconstrained height. |
|
810 static nscoord |
|
811 CalcUnpaginagedHeight(nsPresContext* aPresContext, |
|
812 nsTableCellFrame& aCellFrame, |
|
813 nsTableFrame& aTableFrame, |
|
814 nscoord aVerticalBorderPadding) |
|
815 { |
|
816 const nsTableCellFrame* firstCellInFlow = |
|
817 static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow()); |
|
818 nsTableFrame* firstTableInFlow = |
|
819 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); |
|
820 nsTableRowFrame* row = |
|
821 static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent()); |
|
822 nsTableRowGroupFrame* firstRGInFlow = |
|
823 static_cast<nsTableRowGroupFrame*>(row->GetParent()); |
|
824 |
|
825 int32_t rowIndex; |
|
826 firstCellInFlow->GetRowIndex(rowIndex); |
|
827 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); |
|
828 nscoord cellSpacing = firstTableInFlow->GetCellSpacingX(); |
|
829 |
|
830 nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding; |
|
831 int32_t rowX; |
|
832 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) { |
|
833 if (rowX > rowIndex + rowSpan - 1) { |
|
834 break; |
|
835 } |
|
836 else if (rowX >= rowIndex) { |
|
837 computedHeight += row->GetUnpaginatedHeight(aPresContext); |
|
838 } |
|
839 } |
|
840 return computedHeight; |
|
841 } |
|
842 |
|
843 nsresult nsTableCellFrame::Reflow(nsPresContext* aPresContext, |
|
844 nsHTMLReflowMetrics& aDesiredSize, |
|
845 const nsHTMLReflowState& aReflowState, |
|
846 nsReflowStatus& aStatus) |
|
847 { |
|
848 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); |
|
849 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
|
850 |
|
851 if (aReflowState.mFlags.mSpecialHeightReflow) { |
|
852 FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); |
|
853 } |
|
854 |
|
855 // see if a special height reflow needs to occur due to having a pct height |
|
856 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); |
|
857 |
|
858 aStatus = NS_FRAME_COMPLETE; |
|
859 nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); |
|
860 |
|
861 nsMargin borderPadding = aReflowState.ComputedPhysicalPadding(); |
|
862 nsMargin border; |
|
863 GetBorderWidth(border); |
|
864 borderPadding += border; |
|
865 |
|
866 nscoord topInset = borderPadding.top; |
|
867 nscoord rightInset = borderPadding.right; |
|
868 nscoord bottomInset = borderPadding.bottom; |
|
869 nscoord leftInset = borderPadding.left; |
|
870 |
|
871 // reduce available space by insets, if we're in a constrained situation |
|
872 availSize.width -= leftInset + rightInset; |
|
873 if (NS_UNCONSTRAINEDSIZE != availSize.height) |
|
874 availSize.height -= topInset + bottomInset; |
|
875 |
|
876 // Try to reflow the child into the available space. It might not |
|
877 // fit or might need continuing. |
|
878 if (availSize.height < 0) |
|
879 availSize.height = 1; |
|
880 |
|
881 nsHTMLReflowMetrics kidSize(aReflowState.GetWritingMode(), aDesiredSize.mFlags); |
|
882 kidSize.Width() = kidSize.Height() = 0; |
|
883 SetPriorAvailWidth(aReflowState.AvailableWidth()); |
|
884 nsIFrame* firstKid = mFrames.FirstChild(); |
|
885 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); |
|
886 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); |
|
887 |
|
888 if (aReflowState.mFlags.mSpecialHeightReflow) { |
|
889 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset); |
|
890 DISPLAY_REFLOW_CHANGE(); |
|
891 } |
|
892 else if (aPresContext->IsPaginated()) { |
|
893 nscoord computedUnpaginatedHeight = |
|
894 CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this, |
|
895 *tableFrame, topInset + bottomInset); |
|
896 if (computedUnpaginatedHeight > 0) { |
|
897 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(computedUnpaginatedHeight); |
|
898 DISPLAY_REFLOW_CHANGE(); |
|
899 } |
|
900 } |
|
901 else { |
|
902 SetHasPctOverHeight(false); |
|
903 } |
|
904 |
|
905 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, |
|
906 availSize); |
|
907 |
|
908 // Don't be a percent height observer if we're in the middle of |
|
909 // special-height reflow, in case we get an accidental NotifyPercentHeight() |
|
910 // call (which we shouldn't honor during special-height reflow) |
|
911 if (!aReflowState.mFlags.mSpecialHeightReflow) { |
|
912 // mPercentHeightObserver is for children of cells in quirks mode, |
|
913 // but only those than are tables in standards mode. NeedsToObserve |
|
914 // will determine how far this is propagated to descendants. |
|
915 kidReflowState.mPercentHeightObserver = this; |
|
916 } |
|
917 // Don't propagate special height reflow state to our kids |
|
918 kidReflowState.mFlags.mSpecialHeightReflow = false; |
|
919 |
|
920 if (aReflowState.mFlags.mSpecialHeightReflow || |
|
921 (FirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { |
|
922 // We need to force the kid to have mVResize set if we've had a |
|
923 // special reflow in the past, since the non-special reflow needs to |
|
924 // resize back to what it was without the special height reflow. |
|
925 kidReflowState.mFlags.mVResize = true; |
|
926 } |
|
927 |
|
928 nsPoint kidOrigin(leftInset, topInset); |
|
929 nsRect origRect = firstKid->GetRect(); |
|
930 nsRect origVisualOverflow = firstKid->GetVisualOverflowRect(); |
|
931 bool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; |
|
932 |
|
933 ReflowChild(firstKid, aPresContext, kidSize, kidReflowState, |
|
934 kidOrigin.x, kidOrigin.y, 0, aStatus); |
|
935 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { |
|
936 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it |
|
937 //XXX should paginate overflow as overflow, but not in this patch (bug 379349) |
|
938 NS_FRAME_SET_INCOMPLETE(aStatus); |
|
939 printf("Set table cell incomplete %p\n", static_cast<void*>(this)); |
|
940 } |
|
941 |
|
942 // XXXbz is this invalidate actually needed, really? |
|
943 if (GetStateBits() & NS_FRAME_IS_DIRTY) { |
|
944 InvalidateFrameSubtree(); |
|
945 } |
|
946 |
|
947 #ifdef DEBUG |
|
948 DebugCheckChildSize(firstKid, kidSize, availSize); |
|
949 #endif |
|
950 |
|
951 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode |
|
952 // see testcase "emptyCells.html" |
|
953 nsIFrame* prevInFlow = GetPrevInFlow(); |
|
954 bool isEmpty; |
|
955 if (prevInFlow) { |
|
956 isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty(); |
|
957 } else { |
|
958 isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid); |
|
959 } |
|
960 SetContentEmpty(isEmpty); |
|
961 |
|
962 // Place the child |
|
963 FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowState, |
|
964 kidOrigin.x, kidOrigin.y, 0); |
|
965 |
|
966 nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow, |
|
967 firstReflow); |
|
968 |
|
969 // first, compute the height which can be set w/o being restricted by aMaxSize.height |
|
970 nscoord cellHeight = kidSize.Height(); |
|
971 |
|
972 if (NS_UNCONSTRAINEDSIZE != cellHeight) { |
|
973 cellHeight += topInset + bottomInset; |
|
974 } |
|
975 |
|
976 // next determine the cell's width |
|
977 nscoord cellWidth = kidSize.Width(); // at this point, we've factored in the cell's style attributes |
|
978 |
|
979 // factor in border and padding |
|
980 if (NS_UNCONSTRAINEDSIZE != cellWidth) { |
|
981 cellWidth += leftInset + rightInset; |
|
982 } |
|
983 |
|
984 // set the cell's desired size and max element size |
|
985 aDesiredSize.Width() = cellWidth; |
|
986 aDesiredSize.Height() = cellHeight; |
|
987 |
|
988 // the overflow area will be computed when the child will be vertically aligned |
|
989 |
|
990 if (aReflowState.mFlags.mSpecialHeightReflow) { |
|
991 if (aDesiredSize.Height() > mRect.height) { |
|
992 // set a bit indicating that the pct height contents exceeded |
|
993 // the height that they could honor in the pass 2 reflow |
|
994 SetHasPctOverHeight(true); |
|
995 } |
|
996 if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { |
|
997 aDesiredSize.Height() = mRect.height; |
|
998 } |
|
999 } |
|
1000 |
|
1001 // If our parent is in initial reflow, it'll handle invalidating our |
|
1002 // entire overflow rect. |
|
1003 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) && |
|
1004 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { |
|
1005 InvalidateFrame(); |
|
1006 } |
|
1007 |
|
1008 // remember the desired size for this reflow |
|
1009 SetDesiredSize(aDesiredSize); |
|
1010 |
|
1011 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
1012 return NS_OK; |
|
1013 } |
|
1014 |
|
1015 /* ----- global methods ----- */ |
|
1016 |
|
1017 NS_QUERYFRAME_HEAD(nsTableCellFrame) |
|
1018 NS_QUERYFRAME_ENTRY(nsTableCellFrame) |
|
1019 NS_QUERYFRAME_ENTRY(nsITableCellLayout) |
|
1020 NS_QUERYFRAME_ENTRY(nsIPercentHeightObserver) |
|
1021 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) |
|
1022 |
|
1023 #ifdef ACCESSIBILITY |
|
1024 a11y::AccType |
|
1025 nsTableCellFrame::AccessibleType() |
|
1026 { |
|
1027 return a11y::eHTMLTableCellType; |
|
1028 } |
|
1029 #endif |
|
1030 |
|
1031 /* This is primarily for editor access via nsITableLayout */ |
|
1032 NS_IMETHODIMP |
|
1033 nsTableCellFrame::GetCellIndexes(int32_t &aRowIndex, int32_t &aColIndex) |
|
1034 { |
|
1035 nsresult res = GetRowIndex(aRowIndex); |
|
1036 if (NS_FAILED(res)) |
|
1037 { |
|
1038 aColIndex = 0; |
|
1039 return res; |
|
1040 } |
|
1041 aColIndex = mColIndex; |
|
1042 return NS_OK; |
|
1043 } |
|
1044 |
|
1045 nsIFrame* |
|
1046 NS_NewTableCellFrame(nsIPresShell* aPresShell, |
|
1047 nsStyleContext* aContext, |
|
1048 bool aIsBorderCollapse) |
|
1049 { |
|
1050 if (aIsBorderCollapse) |
|
1051 return new (aPresShell) nsBCTableCellFrame(aContext); |
|
1052 else |
|
1053 return new (aPresShell) nsTableCellFrame(aContext); |
|
1054 } |
|
1055 |
|
1056 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame) |
|
1057 |
|
1058 nsMargin* |
|
1059 nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const |
|
1060 { |
|
1061 aBorder = StyleBorder()->GetComputedBorder(); |
|
1062 return &aBorder; |
|
1063 } |
|
1064 |
|
1065 nsIAtom* |
|
1066 nsTableCellFrame::GetType() const |
|
1067 { |
|
1068 return nsGkAtoms::tableCellFrame; |
|
1069 } |
|
1070 |
|
1071 #ifdef DEBUG_FRAME_DUMP |
|
1072 nsresult |
|
1073 nsTableCellFrame::GetFrameName(nsAString& aResult) const |
|
1074 { |
|
1075 return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult); |
|
1076 } |
|
1077 #endif |
|
1078 |
|
1079 // nsBCTableCellFrame |
|
1080 |
|
1081 nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext) |
|
1082 :nsTableCellFrame(aContext) |
|
1083 { |
|
1084 mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0; |
|
1085 } |
|
1086 |
|
1087 nsBCTableCellFrame::~nsBCTableCellFrame() |
|
1088 { |
|
1089 } |
|
1090 |
|
1091 nsIAtom* |
|
1092 nsBCTableCellFrame::GetType() const |
|
1093 { |
|
1094 return nsGkAtoms::bcTableCellFrame; |
|
1095 } |
|
1096 |
|
1097 /* virtual */ nsMargin |
|
1098 nsBCTableCellFrame::GetUsedBorder() const |
|
1099 { |
|
1100 nsMargin result; |
|
1101 GetBorderWidth(result); |
|
1102 return result; |
|
1103 } |
|
1104 |
|
1105 /* virtual */ bool |
|
1106 nsBCTableCellFrame::GetBorderRadii(nscoord aRadii[8]) const |
|
1107 { |
|
1108 NS_FOR_CSS_HALF_CORNERS(corner) { |
|
1109 aRadii[corner] = 0; |
|
1110 } |
|
1111 return false; |
|
1112 } |
|
1113 |
|
1114 #ifdef DEBUG_FRAME_DUMP |
|
1115 nsresult |
|
1116 nsBCTableCellFrame::GetFrameName(nsAString& aResult) const |
|
1117 { |
|
1118 return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult); |
|
1119 } |
|
1120 #endif |
|
1121 |
|
1122 nsMargin* |
|
1123 nsBCTableCellFrame::GetBorderWidth(nsMargin& aBorder) const |
|
1124 { |
|
1125 int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); |
|
1126 aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, mTopBorder); |
|
1127 aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, mRightBorder); |
|
1128 aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mBottomBorder); |
|
1129 aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mLeftBorder); |
|
1130 return &aBorder; |
|
1131 } |
|
1132 |
|
1133 BCPixelSize |
|
1134 nsBCTableCellFrame::GetBorderWidth(mozilla::css::Side aSide) const |
|
1135 { |
|
1136 switch(aSide) { |
|
1137 case NS_SIDE_TOP: |
|
1138 return BC_BORDER_BOTTOM_HALF(mTopBorder); |
|
1139 case NS_SIDE_RIGHT: |
|
1140 return BC_BORDER_LEFT_HALF(mRightBorder); |
|
1141 case NS_SIDE_BOTTOM: |
|
1142 return BC_BORDER_TOP_HALF(mBottomBorder); |
|
1143 default: |
|
1144 return BC_BORDER_RIGHT_HALF(mLeftBorder); |
|
1145 } |
|
1146 } |
|
1147 |
|
1148 void |
|
1149 nsBCTableCellFrame::SetBorderWidth(mozilla::css::Side aSide, |
|
1150 BCPixelSize aValue) |
|
1151 { |
|
1152 switch(aSide) { |
|
1153 case NS_SIDE_TOP: |
|
1154 mTopBorder = aValue; |
|
1155 break; |
|
1156 case NS_SIDE_RIGHT: |
|
1157 mRightBorder = aValue; |
|
1158 break; |
|
1159 case NS_SIDE_BOTTOM: |
|
1160 mBottomBorder = aValue; |
|
1161 break; |
|
1162 default: |
|
1163 mLeftBorder = aValue; |
|
1164 } |
|
1165 } |
|
1166 |
|
1167 /* virtual */ nsMargin |
|
1168 nsBCTableCellFrame::GetBorderOverflow() |
|
1169 { |
|
1170 nsMargin halfBorder; |
|
1171 int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); |
|
1172 halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder); |
|
1173 halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder); |
|
1174 halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder); |
|
1175 halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder); |
|
1176 return halfBorder; |
|
1177 } |
|
1178 |
|
1179 |
|
1180 void |
|
1181 nsBCTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext, |
|
1182 const nsRect& aDirtyRect, |
|
1183 nsPoint aPt, |
|
1184 uint32_t aFlags) |
|
1185 { |
|
1186 // make border-width reflect the half of the border-collapse |
|
1187 // assigned border that's inside the cell |
|
1188 nsMargin borderWidth; |
|
1189 GetBorderWidth(borderWidth); |
|
1190 |
|
1191 nsStyleBorder myBorder(*StyleBorder()); |
|
1192 |
|
1193 NS_FOR_CSS_SIDES(side) { |
|
1194 myBorder.SetBorderWidth(side, borderWidth.Side(side)); |
|
1195 } |
|
1196 |
|
1197 nsRect rect(aPt, GetSize()); |
|
1198 // bypassing nsCSSRendering::PaintBackground is safe because this kind |
|
1199 // of frame cannot be used for the root element |
|
1200 nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext, this, |
|
1201 aDirtyRect, rect, |
|
1202 StyleContext(), myBorder, |
|
1203 aFlags, nullptr); |
|
1204 } |