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