nsFrameSetFrame.cpp (54731B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* rendering object for HTML <frameset> elements */ 8 9 #include "nsFrameSetFrame.h" 10 11 #include "gfxContext.h" 12 #include "gfxUtils.h" 13 #include "mozAutoDocUpdate.h" 14 #include "mozilla/ComputedStyle.h" 15 #include "mozilla/LookAndFeel.h" 16 #include "mozilla/MouseEvents.h" 17 #include "mozilla/Preferences.h" 18 #include "mozilla/PresShell.h" 19 #include "mozilla/PresShellInlines.h" 20 #include "mozilla/ServoStyleSet.h" 21 #include "mozilla/ServoStyleSetInlines.h" 22 #include "mozilla/dom/ChildIterator.h" 23 #include "mozilla/dom/Element.h" 24 #include "mozilla/dom/HTMLFrameSetElement.h" 25 #include "mozilla/gfx/2D.h" 26 #include "mozilla/gfx/Helpers.h" 27 #include "nsAttrValueInlines.h" 28 #include "nsCSSAnonBoxes.h" 29 #include "nsContainerFrame.h" 30 #include "nsDisplayList.h" 31 #include "nsGenericHTMLElement.h" 32 #include "nsGkAtoms.h" 33 #include "nsHTMLParts.h" 34 #include "nsIContentInlines.h" 35 #include "nsLayoutUtils.h" 36 #include "nsLeafFrame.h" 37 #include "nsNameSpaceManager.h" 38 #include "nsPresContext.h" 39 #include "nsStyleConsts.h" 40 #include "nsSubDocumentFrame.h" 41 42 using namespace mozilla; 43 using namespace mozilla::dom; 44 using namespace mozilla::gfx; 45 46 // masks for mEdgeVisibility 47 #define LEFT_VIS 0x0001 48 #define RIGHT_VIS 0x0002 49 #define TOP_VIS 0x0004 50 #define BOTTOM_VIS 0x0008 51 #define ALL_VIS 0x000F 52 #define NONE_VIS 0x0000 53 54 /******************************************************************************* 55 * nsFramesetDrag 56 ******************************************************************************/ 57 nsFramesetDrag::nsFramesetDrag() { UnSet(); } 58 59 void nsFramesetDrag::Reset(bool aVertical, int32_t aIndex, int32_t aChange, 60 nsHTMLFramesetFrame* aSource) { 61 mVertical = aVertical; 62 mIndex = aIndex; 63 mChange = aChange; 64 mSource = aSource; 65 } 66 67 void nsFramesetDrag::UnSet() { 68 mVertical = true; 69 mIndex = -1; 70 mChange = 0; 71 mSource = nullptr; 72 } 73 74 /******************************************************************************* 75 * nsHTMLFramesetBorderFrame 76 ******************************************************************************/ 77 class nsHTMLFramesetBorderFrame final : public nsLeafFrame { 78 public: 79 NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame) 80 81 #ifdef DEBUG_FRAME_DUMP 82 virtual nsresult GetFrameName(nsAString& aResult) const override; 83 #endif 84 85 virtual nsresult HandleEvent(nsPresContext* aPresContext, 86 WidgetGUIEvent* aEvent, 87 nsEventStatus* aEventStatus) override; 88 89 Cursor GetCursor(const nsPoint&) override; 90 91 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, 92 const nsDisplayListSet& aLists) override; 93 94 virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, 95 const ReflowInput& aReflowInput, 96 nsReflowStatus& aStatus) override; 97 98 bool GetVisibility() { return mVisibility; } 99 void SetVisibility(bool aVisibility); 100 void SetColor(nscolor aColor); 101 102 void PaintBorder(DrawTarget* aDrawTarget, nsPoint aPt); 103 104 protected: 105 nsHTMLFramesetBorderFrame(ComputedStyle*, nsPresContext*, int32_t aWidth, 106 bool aVertical, bool aVisible); 107 virtual ~nsHTMLFramesetBorderFrame(); 108 109 // the prev and next neighbors are indexes into the row (for a horizontal 110 // border) or col (for a vertical border) of nsHTMLFramesetFrames or 111 // nsHTMLFrames 112 int32_t mPrevNeighbor; 113 int32_t mNextNeighbor; 114 nscolor mColor; 115 int32_t mWidth; 116 bool mVertical; 117 bool mVisibility; 118 bool mCanResize; 119 friend class nsHTMLFramesetFrame; 120 }; 121 /******************************************************************************* 122 * nsHTMLFramesetBlankFrame 123 ******************************************************************************/ 124 class nsHTMLFramesetBlankFrame final : public nsLeafFrame { 125 public: 126 NS_DECL_QUERYFRAME 127 NS_DECL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame) 128 129 #ifdef DEBUG_FRAME_DUMP 130 virtual nsresult GetFrameName(nsAString& aResult) const override { 131 return MakeFrameName(u"FramesetBlank"_ns, aResult); 132 } 133 #endif 134 135 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, 136 const nsDisplayListSet& aLists) override; 137 138 virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, 139 const ReflowInput& aReflowInput, 140 nsReflowStatus& aStatus) override; 141 142 protected: 143 explicit nsHTMLFramesetBlankFrame(ComputedStyle* aStyle, 144 nsPresContext* aPresContext) 145 : nsLeafFrame(aStyle, aPresContext, kClassID) {} 146 147 virtual ~nsHTMLFramesetBlankFrame(); 148 149 friend class nsHTMLFramesetFrame; 150 friend class nsHTMLFrameset; 151 }; 152 153 /******************************************************************************* 154 * nsHTMLFramesetFrame 155 ******************************************************************************/ 156 bool nsHTMLFramesetFrame::gDragInProgress = false; 157 #define DEFAULT_BORDER_WIDTH_PX 6 158 159 nsHTMLFramesetFrame::nsHTMLFramesetFrame(ComputedStyle* aStyle, 160 nsPresContext* aPresContext) 161 : nsContainerFrame(aStyle, aPresContext, kClassID) { 162 mEdgeVisibility = 0; 163 mParentFrameborder = eFrameborder_Yes; // default 164 mParentBorderWidth = -1; // default not set 165 mParentBorderColor = NO_COLOR; // default not set 166 mFirstDragPoint.x = mFirstDragPoint.y = 0; 167 mMinDrag = nsPresContext::CSSPixelsToAppUnits(2); 168 mNonBorderChildCount = 0; 169 mNonBlankChildCount = 0; 170 mDragger = nullptr; 171 mChildCount = 0; 172 mTopLevelFrameset = nullptr; 173 mEdgeColors.Set(NO_COLOR); 174 } 175 176 nsHTMLFramesetFrame::~nsHTMLFramesetFrame() = default; 177 178 NS_QUERYFRAME_HEAD(nsHTMLFramesetFrame) 179 NS_QUERYFRAME_ENTRY(nsHTMLFramesetFrame) 180 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 181 182 void nsHTMLFramesetFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, 183 nsIFrame* aPrevInFlow) { 184 nsContainerFrame::Init(aContent, aParent, aPrevInFlow); 185 // find the highest ancestor that is a frameset 186 nsIFrame* parentFrame = GetParent(); 187 mTopLevelFrameset = this; 188 while (parentFrame) { 189 nsHTMLFramesetFrame* frameset = do_QueryFrame(parentFrame); 190 if (frameset) { 191 mTopLevelFrameset = frameset; 192 parentFrame = parentFrame->GetParent(); 193 } else { 194 break; 195 } 196 } 197 198 nsPresContext* presContext = PresContext(); 199 mozilla::PresShell* presShell = presContext->PresShell(); 200 201 nsFrameborder frameborder = GetFrameBorder(); 202 int32_t borderWidth = GetBorderWidth(presContext, false); 203 nscolor borderColor = GetBorderColor(); 204 205 // Get the rows= cols= data 206 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent); 207 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!"); 208 auto rowSpecs = ourContent->GetRowSpec(); 209 auto colSpecs = ourContent->GetColSpec(); 210 211 static_assert( 212 NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscoord), 213 "Maximum value of NumRows() and NumCols() is NS_MAX_FRAMESET_SPEC_COUNT"); 214 mRowSizes.Clear(); 215 mRowSizes.SetLength(rowSpecs.Length()); 216 mColSizes.Clear(); 217 mColSizes.SetLength(colSpecs.Length()); 218 219 static_assert( 220 NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT, 221 "Should not overflow numCells"); 222 int32_t numCells = NumRows() * NumCols(); 223 224 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < 225 UINT_MAX / sizeof(nsHTMLFramesetBorderFrame*), 226 "Should not overflow nsHTMLFramesetBorderFrame"); 227 mVerBorders.Clear(); 228 mVerBorders.SetLength(NumCols()); // 1 more than number of ver borders 229 230 for (int32_t verX = 0; verX < NumCols(); verX++) { 231 mVerBorders[verX] = nullptr; 232 } 233 234 mHorBorders.Clear(); 235 mHorBorders.SetLength(NumRows()); // 1 more than number of hor borders 236 237 for (int32_t horX = 0; horX < NumRows(); horX++) { 238 mHorBorders[horX] = nullptr; 239 } 240 241 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < 242 UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT, 243 "Should not overflow numCells"); 244 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nsFrameborder) / 245 NS_MAX_FRAMESET_SPEC_COUNT, 246 "Should not overflow numCells"); 247 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nsBorderColor) / 248 NS_MAX_FRAMESET_SPEC_COUNT, 249 "Should not overflow numCells"); 250 mNeedFirstReflowWork = true; 251 mChildFrameborder.Clear(); 252 mChildFrameborder.SetLength(numCells); 253 mChildBorderColors.Clear(); 254 mChildBorderColors.SetLength(numCells); 255 256 // create the children frames; skip content which isn't <frameset> or <frame> 257 mChildCount = 0; // number of <frame> or <frameset> children 258 259 FlattenedChildIterator children(mContent); 260 for (nsIContent* child = children.GetNextChild(); child; 261 child = children.GetNextChild()) { 262 if (mChildCount == numCells) { 263 // we have more <frame> or <frameset> than cells 264 // Clear the lazy bits in the remaining children. Also clear 265 // the restyle flags, like nsCSSFrameConstructor::ProcessChildren does. 266 for (; child; child = child->GetNextSibling()) { 267 child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); 268 } 269 break; 270 } 271 child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME); 272 273 // IMPORTANT: This must match the conditions in 274 // nsCSSFrameConstructor::ContentAppended/Inserted/Removed 275 if (!child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) { 276 continue; 277 } 278 279 // FIXME(emilio): This doesn't even respect display: none, but that matches 280 // other browsers ;_; 281 // 282 // Maybe we should change that though. 283 RefPtr<ComputedStyle> kidStyle = 284 ServoStyleSet::ResolveServoStyle(*child->AsElement()); 285 nsIFrame* frame; 286 if (child->IsHTMLElement(nsGkAtoms::frameset)) { 287 frame = NS_NewHTMLFramesetFrame(presShell, kidStyle); 288 289 nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame; 290 childFrame->SetParentFrameborder(frameborder); 291 childFrame->SetParentBorderWidth(borderWidth); 292 childFrame->SetParentBorderColor(borderColor); 293 frame->Init(child, this, nullptr); 294 295 mChildBorderColors[mChildCount].Set(childFrame->GetBorderColor()); 296 } else { // frame 297 frame = NS_NewSubDocumentFrame(presShell, kidStyle); 298 299 frame->Init(child, this, nullptr); 300 301 mChildFrameborder[mChildCount] = GetFrameBorder(child); 302 mChildBorderColors[mChildCount].Set(GetBorderColor(child)); 303 } 304 child->SetPrimaryFrame(frame); 305 306 mFrames.AppendFrame(nullptr, frame); 307 308 mChildCount++; 309 } 310 311 mNonBlankChildCount = mChildCount; 312 // add blank frames for frameset cells that had no content provided 313 for (int blankX = mChildCount; blankX < numCells; blankX++) { 314 RefPtr<ComputedStyle> pseudoComputedStyle = 315 presShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle( 316 PseudoStyleType::framesetBlank); 317 318 // XXX the blank frame is using the content of its parent - at some point it 319 // should just have null content, if we support that 320 nsHTMLFramesetBlankFrame* blankFrame = new (presShell) 321 nsHTMLFramesetBlankFrame(pseudoComputedStyle, PresContext()); 322 323 blankFrame->Init(mContent, this, nullptr); 324 325 mFrames.AppendFrame(nullptr, blankFrame); 326 327 mChildBorderColors[mChildCount].Set(NO_COLOR); 328 mChildCount++; 329 } 330 331 mNonBorderChildCount = mChildCount; 332 } 333 334 void nsHTMLFramesetFrame::SetInitialChildList(ChildListID aListID, 335 nsFrameList&& aChildList) { 336 // We do this weirdness where we create our child frames in Init(). On the 337 // other hand, we're going to get a SetInitialChildList() with an empty list 338 // and null list name after the frame constructor is done creating us. So 339 // just ignore that call. 340 if (aListID == FrameChildListID::Principal && aChildList.IsEmpty()) { 341 return; 342 } 343 344 nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList)); 345 } 346 347 // XXX should this try to allocate twips based on an even pixel boundary? 348 void nsHTMLFramesetFrame::Scale(nscoord aDesired, int32_t aNumIndicies, 349 const nsTArray<int32_t>& aIndicies, 350 nsTArray<int32_t>& aItems) { 351 int32_t actual = 0; 352 // get the actual total 353 for (int32_t i = 0; i < aNumIndicies; i++) { 354 int32_t j = aIndicies[i]; 355 actual += aItems[j]; 356 } 357 358 if (actual > 0) { 359 float factor = (float)aDesired / (float)actual; 360 actual = 0; 361 // scale the items up or down 362 for (int32_t i = 0; i < aNumIndicies; i++) { 363 int32_t j = aIndicies[i]; 364 aItems[j] = NSToCoordRound((float)aItems[j] * factor); 365 actual += aItems[j]; 366 } 367 } else if (aNumIndicies != 0) { 368 // All the specs say zero width, but we have to fill up space 369 // somehow. Distribute it equally. 370 nscoord width = NSToCoordRound((float)aDesired / (float)aNumIndicies); 371 actual = width * aNumIndicies; 372 for (int32_t i = 0; i < aNumIndicies; i++) { 373 aItems[aIndicies[i]] = width; 374 } 375 } 376 377 if (aNumIndicies > 0 && aDesired != actual) { 378 int32_t unit = (aDesired > actual) ? 1 : -1; 379 for (int32_t i = 0; (i < aNumIndicies) && (aDesired != actual); i++) { 380 int32_t j = aIndicies[i]; 381 if (CheckedInt<size_t>(j).value() < aItems.Length()) { 382 aItems[j] += unit; 383 actual += unit; 384 } 385 } 386 } 387 } 388 389 /** 390 * Translate the rows/cols specs into an array of integer sizes for 391 * each cell in the frameset. Sizes are allocated based on the priorities of the 392 * specifier - fixed sizes have the highest priority, percentage sizes have the 393 * next highest priority and relative sizes have the lowest. 394 */ 395 void nsHTMLFramesetFrame::CalculateRowCol( 396 nsPresContext* aPresContext, nscoord aSize, 397 const mozilla::Span<const nsFramesetSpec>& aSpecs, 398 nsTArray<nscoord>& aValues) { 399 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(int32_t), 400 "aNumSpecs maximum value is NS_MAX_FRAMESET_SPEC_COUNT"); 401 MOZ_ASSERT(aSpecs.Length() == aValues.Length()); 402 403 int32_t fixedTotal = 0; 404 int32_t numFixed = 0; 405 nsTArray<int32_t> fixed; 406 fixed.SetLength(aSpecs.Length()); 407 int32_t numPercent = 0; 408 nsTArray<int32_t> percent; 409 percent.SetLength(aSpecs.Length()); 410 int32_t relativeSums = 0; 411 int32_t numRelative = 0; 412 nsTArray<int32_t> relative; 413 relative.SetLength(aSpecs.Length()); 414 415 // initialize the fixed, percent, relative indices, allocate the fixed sizes 416 // and zero the others 417 for (int32_t i = 0; i < CheckedInt<int32_t>(aSpecs.Length()).value(); i++) { 418 aValues[i] = 0; 419 switch (aSpecs[i].mUnit) { 420 case eFramesetUnit_Fixed: 421 aValues[i] = nsPresContext::CSSPixelsToAppUnits(aSpecs[i].mValue); 422 fixedTotal += aValues[i]; 423 fixed[numFixed] = i; 424 numFixed++; 425 break; 426 case eFramesetUnit_Percent: 427 percent[numPercent] = i; 428 numPercent++; 429 break; 430 case eFramesetUnit_Relative: 431 relative[numRelative] = i; 432 numRelative++; 433 relativeSums += aSpecs[i].mValue; 434 break; 435 } 436 } 437 438 // scale the fixed sizes if they total too much (or too little and there 439 // aren't any percent or relative) 440 if ((fixedTotal > aSize) || 441 ((fixedTotal < aSize) && (0 == numPercent) && (0 == numRelative))) { 442 Scale(aSize, numFixed, fixed, aValues); 443 return; 444 } 445 446 int32_t percentMax = aSize - fixedTotal; 447 int32_t percentTotal = 0; 448 // allocate the percentage sizes from what is left over from the fixed 449 // allocation 450 for (int32_t i = 0; i < numPercent; i++) { 451 int32_t j = percent[i]; 452 aValues[j] = 453 NSToCoordRound((float)aSpecs[j].mValue * (float)aSize / 100.0f); 454 percentTotal += aValues[j]; 455 } 456 457 // scale the percent sizes if they total too much (or too little and there 458 // aren't any relative) 459 if ((percentTotal > percentMax) || 460 ((percentTotal < percentMax) && (0 == numRelative))) { 461 Scale(percentMax, numPercent, percent, aValues); 462 return; 463 } 464 465 int32_t relativeMax = percentMax - percentTotal; 466 int32_t relativeTotal = 0; 467 // allocate the relative sizes from what is left over from the percent 468 // allocation 469 for (int32_t i = 0; i < numRelative; i++) { 470 int32_t j = relative[i]; 471 aValues[j] = NSToCoordRound((float)aSpecs[j].mValue * (float)relativeMax / 472 (float)relativeSums); 473 relativeTotal += aValues[j]; 474 } 475 476 // scale the relative sizes if they take up too much or too little 477 if (relativeTotal != relativeMax) { 478 Scale(relativeMax, numRelative, relative, aValues); 479 } 480 } 481 482 /** 483 * Translate the rows/cols integer sizes into an array of specs for 484 * each cell in the frameset. Reverse of CalculateRowCol() behaviour. 485 * This allows us to maintain the user size info through reflows. 486 */ 487 void nsHTMLFramesetFrame::GenerateRowCol( 488 nsPresContext* aPresContext, nscoord aSize, 489 const mozilla::Span<const nsFramesetSpec>& aSpecs, 490 const nsTArray<nscoord>& aValues, nsString& aNewAttr) { 491 MOZ_ASSERT(aSpecs.Length() == aValues.Length()); 492 493 for (size_t i = 0; i < aSpecs.Length(); i++) { 494 if (!aNewAttr.IsEmpty()) { 495 aNewAttr.Append(char16_t(',')); 496 } 497 498 switch (aSpecs[i].mUnit) { 499 case eFramesetUnit_Fixed: 500 aNewAttr.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(aValues[i])); 501 break; 502 case eFramesetUnit_Percent: // XXX Only accurate to 1%, need 1 pixel 503 case eFramesetUnit_Relative: 504 // Add 0.5 to the percentage to make rounding work right. 505 aNewAttr.AppendInt(uint32_t((100.0 * aValues[i]) / aSize + 0.5)); 506 aNewAttr.Append(char16_t('%')); 507 break; 508 } 509 } 510 } 511 512 int32_t nsHTMLFramesetFrame::GetBorderWidth(nsPresContext* aPresContext, 513 bool aTakeForcingIntoAccount) { 514 nsFrameborder frameborder = GetFrameBorder(); 515 if (frameborder == eFrameborder_No) { 516 return 0; 517 } 518 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent); 519 520 if (content) { 521 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::border); 522 if (attr) { 523 int32_t intVal = 0; 524 if (attr->Type() == nsAttrValue::eInteger) { 525 intVal = attr->GetIntegerValue(); 526 if (intVal < 0) { 527 intVal = 0; 528 } 529 } 530 531 return nsPresContext::CSSPixelsToAppUnits(intVal); 532 } 533 } 534 535 if (mParentBorderWidth >= 0) { 536 return mParentBorderWidth; 537 } 538 539 return nsPresContext::CSSPixelsToAppUnits(DEFAULT_BORDER_WIDTH_PX); 540 } 541 542 void nsHTMLFramesetFrame::GetDesiredSize(nsPresContext* aPresContext, 543 const ReflowInput& aReflowInput, 544 ReflowOutput& aDesiredSize) { 545 WritingMode wm = aReflowInput.GetWritingMode(); 546 LogicalSize desiredSize(wm); 547 nsHTMLFramesetFrame* framesetParent = do_QueryFrame(GetParent()); 548 if (nullptr == framesetParent) { 549 if (aPresContext->IsPaginated()) { 550 // XXX This needs to be changed when framesets paginate properly 551 desiredSize.ISize(wm) = aReflowInput.AvailableISize(); 552 desiredSize.BSize(wm) = aReflowInput.AvailableBSize(); 553 } else { 554 LogicalSize area(wm, aPresContext->GetVisibleArea().Size()); 555 556 desiredSize.ISize(wm) = area.ISize(wm); 557 desiredSize.BSize(wm) = area.BSize(wm); 558 } 559 } else { 560 LogicalSize size(wm); 561 framesetParent->GetSizeOfChild(this, wm, size); 562 desiredSize.ISize(wm) = size.ISize(wm); 563 desiredSize.BSize(wm) = size.BSize(wm); 564 } 565 aDesiredSize.SetSize(wm, desiredSize); 566 } 567 568 // only valid for non border children 569 void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t aIndexInParent, 570 WritingMode aWM, LogicalSize& aSize, 571 nsIntPoint& aCellIndex) { 572 int32_t row = aIndexInParent / NumCols(); 573 int32_t col = 574 aIndexInParent - 575 (row * NumCols()); // remainder from dividing index by NumCols() 576 if ((row < NumRows()) && (col < NumCols())) { 577 aSize.ISize(aWM) = mColSizes[col]; 578 aSize.BSize(aWM) = mRowSizes[row]; 579 aCellIndex.x = col; 580 aCellIndex.y = row; 581 } else { 582 aSize.SizeTo(aWM, 0, 0); 583 aCellIndex.x = aCellIndex.y = 0; 584 } 585 } 586 587 // only valid for non border children 588 void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild, WritingMode aWM, 589 LogicalSize& aSize) { 590 // Reflow only creates children frames for <frameset> and <frame> content. 591 // this assumption is used here 592 int i = 0; 593 for (nsIFrame* child : mFrames) { 594 if (aChild == child) { 595 nsIntPoint ignore; 596 GetSizeOfChildAt(i, aWM, aSize, ignore); 597 return; 598 } 599 i++; 600 } 601 aSize.SizeTo(aWM, 0, 0); 602 } 603 604 nsresult nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext, 605 WidgetGUIEvent* aEvent, 606 nsEventStatus* aEventStatus) { 607 NS_ENSURE_ARG_POINTER(aEventStatus); 608 if (mDragger) { 609 // the nsFramesetBorderFrame has captured NS_MOUSE_DOWN 610 switch (aEvent->mMessage) { 611 case eMouseMove: 612 MouseDrag(aPresContext, aEvent); 613 break; 614 case eMouseUp: 615 if (aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) { 616 EndMouseDrag(aPresContext); 617 } 618 break; 619 default: 620 break; 621 } 622 *aEventStatus = nsEventStatus_eConsumeNoDefault; 623 } else { 624 *aEventStatus = nsEventStatus_eIgnore; 625 } 626 return NS_OK; 627 } 628 629 nsIFrame::Cursor nsHTMLFramesetFrame::GetCursor(const nsPoint&) { 630 auto kind = StyleCursorKind::Default; 631 if (mDragger) { 632 kind = mDragger->mVertical ? StyleCursorKind::EwResize 633 : StyleCursorKind::NsResize; 634 } 635 return Cursor{kind, AllowCustomCursorImage::No}; 636 } 637 638 void nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 639 const nsDisplayListSet& aLists) { 640 BuildDisplayListForInline(aBuilder, aLists); 641 642 if (mDragger && aBuilder->IsForEventDelivery()) { 643 aLists.Content()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder, this); 644 } 645 } 646 647 void nsHTMLFramesetFrame::ReflowPlaceChild(nsIFrame* aChild, 648 nsPresContext* aPresContext, 649 const ReflowInput& aReflowInput, 650 nsPoint& aOffset, nsSize& aSize, 651 nsIntPoint* aCellIndex) { 652 // reflow the child 653 ReflowInput reflowInput(aPresContext, aReflowInput, aChild, 654 LogicalSize(aChild->GetWritingMode(), aSize)); 655 reflowInput.SetComputedWidth(std::max( 656 0, 657 aSize.width - reflowInput.ComputedPhysicalBorderPadding().LeftRight())); 658 reflowInput.SetComputedHeight(std::max( 659 0, 660 aSize.height - reflowInput.ComputedPhysicalBorderPadding().TopBottom())); 661 ReflowOutput reflowOutput(aReflowInput); 662 reflowOutput.Width() = aSize.width; 663 reflowOutput.Height() = aSize.height; 664 nsReflowStatus status; 665 666 ReflowChild(aChild, aPresContext, reflowOutput, reflowInput, aOffset.x, 667 aOffset.y, ReflowChildFlags::Default, status); 668 NS_ASSERTION(status.IsComplete(), "bad status"); 669 670 // Place and size the child 671 reflowOutput.Width() = aSize.width; 672 reflowOutput.Height() = aSize.height; 673 FinishReflowChild(aChild, aPresContext, reflowOutput, &reflowInput, aOffset.x, 674 aOffset.y, ReflowChildFlags::Default); 675 } 676 677 static nsFrameborder GetFrameBorderHelper(nsGenericHTMLElement* aContent) { 678 if (nullptr != aContent) { 679 const nsAttrValue* attr = aContent->GetParsedAttr(nsGkAtoms::frameborder); 680 if (attr && attr->Type() == nsAttrValue::eEnum) { 681 switch (static_cast<FrameBorderProperty>(attr->GetEnumValue())) { 682 case FrameBorderProperty::Yes: 683 case FrameBorderProperty::One: 684 return eFrameborder_Yes; 685 686 case FrameBorderProperty::No: 687 case FrameBorderProperty::Zero: 688 return eFrameborder_No; 689 } 690 } 691 } 692 return eFrameborder_Notset; 693 } 694 695 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder() { 696 nsFrameborder result = eFrameborder_Notset; 697 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent); 698 699 if (content) { 700 result = GetFrameBorderHelper(content); 701 } 702 if (eFrameborder_Notset == result) { 703 return mParentFrameborder; 704 } 705 return result; 706 } 707 708 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder(nsIContent* aContent) { 709 nsFrameborder result = eFrameborder_Notset; 710 711 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(aContent); 712 713 if (content) { 714 result = GetFrameBorderHelper(content); 715 } 716 if (eFrameborder_Notset == result) { 717 return GetFrameBorder(); 718 } 719 return result; 720 } 721 722 nscolor nsHTMLFramesetFrame::GetBorderColor() { 723 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(mContent); 724 725 if (content) { 726 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor); 727 if (attr) { 728 nscolor color; 729 if (attr->GetColorValue(color)) { 730 return color; 731 } 732 } 733 } 734 735 return mParentBorderColor; 736 } 737 738 nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent) { 739 nsGenericHTMLElement* content = nsGenericHTMLElement::FromNode(aContent); 740 741 if (content) { 742 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor); 743 if (attr) { 744 nscolor color; 745 if (attr->GetColorValue(color)) { 746 return color; 747 } 748 } 749 } 750 return GetBorderColor(); 751 } 752 753 void nsHTMLFramesetFrame::Reflow(nsPresContext* aPresContext, 754 ReflowOutput& aDesiredSize, 755 const ReflowInput& aReflowInput, 756 nsReflowStatus& aStatus) { 757 MarkInReflow(); 758 DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame"); 759 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 760 761 mozilla::PresShell* presShell = aPresContext->PresShell(); 762 ServoStyleSet* styleSet = presShell->StyleSet(); 763 764 GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 765 766 // printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, 767 // aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()); 768 // Always get the size so that the caller knows how big we are 769 GetDesiredSize(aPresContext, aReflowInput, aDesiredSize); 770 771 nscoord width = (aDesiredSize.Width() <= aReflowInput.AvailableWidth()) 772 ? aDesiredSize.Width() 773 : aReflowInput.AvailableWidth(); 774 nscoord height = (aDesiredSize.Height() <= aReflowInput.AvailableHeight()) 775 ? aDesiredSize.Height() 776 : aReflowInput.AvailableHeight(); 777 778 // We might be reflowed more than once with NS_FRAME_FIRST_REFLOW; 779 // that's allowed. (Though it will only happen for misuse of frameset 780 // that includes it within other content.) So measure firstTime by 781 // what we care about, which is whether we've processed the data we 782 // process below if firstTime is true. We can't assert that IsEmpty() 783 // is the same for the 2 child arrays, because they can also end up 784 // empty in Init() if they hit an error state. 785 MOZ_ASSERT_IF(!mChildFrameborder.IsEmpty(), mNeedFirstReflowWork); 786 MOZ_ASSERT_IF(!mChildBorderColors.IsEmpty(), mNeedFirstReflowWork); 787 bool firstTime = mNeedFirstReflowWork; 788 789 // subtract out the width of all of the potential borders. There are 790 // only borders between <frame>s. There are none on the edges (e.g the 791 // leftmost <frame> has no left border). 792 int32_t borderWidth = GetBorderWidth(aPresContext, true); 793 794 width -= (NumCols() - 1) * borderWidth; 795 if (width < 0) { 796 width = 0; 797 } 798 799 height -= (NumRows() - 1) * borderWidth; 800 if (height < 0) { 801 height = 0; 802 } 803 804 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent); 805 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!"); 806 auto rowSpecs = ourContent->GetRowSpec(); 807 auto colSpecs = ourContent->GetColSpec(); 808 // If the number of cols or rows has changed, the frame for the frameset 809 // will be re-created. 810 if (CheckedInt<size_t>(NumRows()).value() != rowSpecs.Length() || 811 CheckedInt<size_t>(NumCols()).value() != colSpecs.Length()) { 812 mDrag.UnSet(); 813 return; 814 } 815 816 CalculateRowCol(aPresContext, width, colSpecs, mColSizes); 817 CalculateRowCol(aPresContext, height, rowSpecs, mRowSizes); 818 819 nsTArray<bool> verBordersVis; // vertical borders visibility 820 nsTArray<nscolor> verBorderColors; 821 nsTArray<bool> horBordersVis; // horizontal borders visibility 822 nsTArray<nscolor> horBorderColors; 823 nscolor borderColor = GetBorderColor(); 824 nsFrameborder frameborder = GetFrameBorder(); 825 826 if (firstTime) { 827 // Check for overflow in memory allocations using NumCols() and NumRows() 828 // which have a maxium value of NS_MAX_FRAMESET_SPEC_COUNT. 829 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(bool), 830 "Check for overflow"); 831 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscolor), 832 "Check for overflow"); 833 834 verBordersVis.SetLength(NumCols()); 835 verBorderColors.SetLength(NumCols()); 836 for (int32_t verX = 0; verX < NumCols(); verX++) { 837 verBordersVis[verX] = false; 838 verBorderColors[verX] = NO_COLOR; 839 } 840 841 horBordersVis.SetLength(NumRows()); 842 horBorderColors.SetLength(NumRows()); 843 for (int32_t horX = 0; horX < NumRows(); horX++) { 844 horBordersVis[horX] = false; 845 horBorderColors[horX] = NO_COLOR; 846 } 847 } 848 849 // reflow the children 850 int32_t lastRow = 0; 851 int32_t lastCol = 0; 852 int32_t borderChildX = mNonBorderChildCount; // index of border children 853 nsHTMLFramesetBorderFrame* borderFrame = nullptr; 854 nsPoint offset(0, 0); 855 nsSize size, lastSize; 856 WritingMode wm = GetWritingMode(); 857 LogicalSize logicalSize(wm); 858 nsIFrame* child = mFrames.FirstChild(); 859 860 for (int32_t childX = 0; childX < mNonBorderChildCount; childX++) { 861 nsIntPoint cellIndex; 862 GetSizeOfChildAt(childX, wm, logicalSize, cellIndex); 863 size = logicalSize.GetPhysicalSize(wm); 864 865 if (lastRow != cellIndex.y) { // changed to next row 866 offset.x = 0; 867 offset.y += lastSize.height; 868 if (firstTime) { // create horizontal border 869 870 RefPtr<ComputedStyle> pseudoComputedStyle; 871 pseudoComputedStyle = styleSet->ResolveNonInheritingAnonymousBoxStyle( 872 PseudoStyleType::horizontalFramesetBorder); 873 874 borderFrame = new (presShell) nsHTMLFramesetBorderFrame( 875 pseudoComputedStyle, PresContext(), borderWidth, false, false); 876 borderFrame->Init(mContent, this, nullptr); 877 mChildCount++; 878 mFrames.AppendFrame(nullptr, borderFrame); 879 mHorBorders[cellIndex.y - 1] = borderFrame; 880 // set the neighbors for determining drag boundaries 881 borderFrame->mPrevNeighbor = lastRow; 882 borderFrame->mNextNeighbor = cellIndex.y; 883 } else { 884 borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX); 885 borderFrame->mWidth = borderWidth; 886 borderChildX++; 887 } 888 nsSize borderSize(aDesiredSize.Width(), borderWidth); 889 ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset, 890 borderSize); 891 borderFrame = nullptr; 892 offset.y += borderWidth; 893 } else { 894 if (cellIndex.x > 0) { // moved to next col in same row 895 if (0 == cellIndex.y) { // in 1st row 896 if (firstTime) { // create vertical border 897 898 RefPtr<ComputedStyle> pseudoComputedStyle; 899 pseudoComputedStyle = 900 styleSet->ResolveNonInheritingAnonymousBoxStyle( 901 PseudoStyleType::verticalFramesetBorder); 902 903 borderFrame = new (presShell) nsHTMLFramesetBorderFrame( 904 pseudoComputedStyle, PresContext(), borderWidth, true, false); 905 borderFrame->Init(mContent, this, nullptr); 906 mChildCount++; 907 mFrames.AppendFrame(nullptr, borderFrame); 908 mVerBorders[cellIndex.x - 1] = borderFrame; 909 // set the neighbors for determining drag boundaries 910 borderFrame->mPrevNeighbor = lastCol; 911 borderFrame->mNextNeighbor = cellIndex.x; 912 } else { 913 borderFrame = 914 (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX); 915 borderFrame->mWidth = borderWidth; 916 borderChildX++; 917 } 918 nsSize borderSize(borderWidth, aDesiredSize.Height()); 919 ReflowPlaceChild(borderFrame, aPresContext, aReflowInput, offset, 920 borderSize); 921 borderFrame = nullptr; 922 } 923 offset.x += borderWidth; 924 } 925 } 926 927 ReflowPlaceChild(child, aPresContext, aReflowInput, offset, size, 928 &cellIndex); 929 930 if (firstTime) { 931 int32_t childVis; 932 nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(child); 933 if (framesetFrame) { 934 childVis = framesetFrame->mEdgeVisibility; 935 mChildBorderColors[childX] = framesetFrame->mEdgeColors; 936 } else if (child->IsSubDocumentFrame()) { 937 if (eFrameborder_Yes == mChildFrameborder[childX]) { 938 childVis = ALL_VIS; 939 } else if (eFrameborder_No == mChildFrameborder[childX]) { 940 childVis = NONE_VIS; 941 } else { // notset 942 childVis = (eFrameborder_No == frameborder) ? NONE_VIS : ALL_VIS; 943 } 944 } else { // blank 945 #ifdef DEBUG 946 nsHTMLFramesetBlankFrame* blank = do_QueryFrame(child); 947 MOZ_ASSERT(blank, "unexpected child frame type"); 948 #endif 949 childVis = NONE_VIS; 950 } 951 nsBorderColor childColors = mChildBorderColors[childX]; 952 // set the visibility, color of our edge borders based on children 953 if (0 == cellIndex.x) { 954 if (!(mEdgeVisibility & LEFT_VIS)) { 955 mEdgeVisibility |= (LEFT_VIS & childVis); 956 } 957 if (NO_COLOR == mEdgeColors.mLeft) { 958 mEdgeColors.mLeft = childColors.mLeft; 959 } 960 } 961 if (0 == cellIndex.y) { 962 if (!(mEdgeVisibility & TOP_VIS)) { 963 mEdgeVisibility |= (TOP_VIS & childVis); 964 } 965 if (NO_COLOR == mEdgeColors.mTop) { 966 mEdgeColors.mTop = childColors.mTop; 967 } 968 } 969 if (NumCols() - 1 == cellIndex.x) { 970 if (!(mEdgeVisibility & RIGHT_VIS)) { 971 mEdgeVisibility |= (RIGHT_VIS & childVis); 972 } 973 if (NO_COLOR == mEdgeColors.mRight) { 974 mEdgeColors.mRight = childColors.mRight; 975 } 976 } 977 if (NumRows() - 1 == cellIndex.y) { 978 if (!(mEdgeVisibility & BOTTOM_VIS)) { 979 mEdgeVisibility |= (BOTTOM_VIS & childVis); 980 } 981 if (NO_COLOR == mEdgeColors.mBottom) { 982 mEdgeColors.mBottom = childColors.mBottom; 983 } 984 } 985 // set the visibility of borders that the child may affect 986 if (childVis & RIGHT_VIS) { 987 verBordersVis[cellIndex.x] = true; 988 } 989 if (childVis & BOTTOM_VIS) { 990 horBordersVis[cellIndex.y] = true; 991 } 992 if ((cellIndex.x > 0) && (childVis & LEFT_VIS)) { 993 verBordersVis[cellIndex.x - 1] = true; 994 } 995 if ((cellIndex.y > 0) && (childVis & TOP_VIS)) { 996 horBordersVis[cellIndex.y - 1] = true; 997 } 998 // set the colors of borders that the child may affect 999 if (NO_COLOR == verBorderColors[cellIndex.x]) { 1000 verBorderColors[cellIndex.x] = mChildBorderColors[childX].mRight; 1001 } 1002 if (NO_COLOR == horBorderColors[cellIndex.y]) { 1003 horBorderColors[cellIndex.y] = mChildBorderColors[childX].mBottom; 1004 } 1005 if ((cellIndex.x > 0) && (NO_COLOR == verBorderColors[cellIndex.x - 1])) { 1006 verBorderColors[cellIndex.x - 1] = mChildBorderColors[childX].mLeft; 1007 } 1008 if ((cellIndex.y > 0) && (NO_COLOR == horBorderColors[cellIndex.y - 1])) { 1009 horBorderColors[cellIndex.y - 1] = mChildBorderColors[childX].mTop; 1010 } 1011 } 1012 lastRow = cellIndex.y; 1013 lastCol = cellIndex.x; 1014 lastSize = size; 1015 offset.x += size.width; 1016 child = child->GetNextSibling(); 1017 } 1018 1019 if (firstTime) { 1020 nscolor childColor; 1021 // set the visibility, color, mouse sensitivity of borders 1022 for (int32_t verX = 0; verX < NumCols() - 1; verX++) { 1023 if (mVerBorders[verX]) { 1024 mVerBorders[verX]->SetVisibility(verBordersVis[verX]); 1025 SetBorderResize(mVerBorders[verX]); 1026 childColor = (NO_COLOR == verBorderColors[verX]) 1027 ? borderColor 1028 : verBorderColors[verX]; 1029 mVerBorders[verX]->SetColor(childColor); 1030 } 1031 } 1032 for (int32_t horX = 0; horX < NumRows() - 1; horX++) { 1033 if (mHorBorders[horX]) { 1034 mHorBorders[horX]->SetVisibility(horBordersVis[horX]); 1035 SetBorderResize(mHorBorders[horX]); 1036 childColor = (NO_COLOR == horBorderColors[horX]) 1037 ? borderColor 1038 : horBorderColors[horX]; 1039 mHorBorders[horX]->SetColor(childColor); 1040 } 1041 } 1042 1043 mNeedFirstReflowWork = false; 1044 mChildFrameborder.Clear(); 1045 mChildBorderColors.Clear(); 1046 } 1047 1048 mDrag.UnSet(); 1049 1050 aDesiredSize.SetOverflowAreasToDesiredBounds(); 1051 FinishAndStoreOverflow(&aDesiredSize); 1052 } 1053 1054 #ifdef DEBUG_FRAME_DUMP 1055 nsresult nsHTMLFramesetFrame::GetFrameName(nsAString& aResult) const { 1056 return MakeFrameName(u"Frameset"_ns, aResult); 1057 } 1058 #endif 1059 1060 bool nsHTMLFramesetFrame::CanResize(bool aVertical, bool aLeft) { 1061 int32_t childX; 1062 int32_t startX; 1063 if (aVertical) { 1064 startX = (aLeft) ? 0 : NumCols() - 1; 1065 for (childX = startX; childX < mNonBorderChildCount; childX += NumCols()) { 1066 if (!CanChildResize(aVertical, aLeft, childX)) { 1067 return false; 1068 } 1069 } 1070 } else { 1071 startX = (aLeft) ? 0 : (NumRows() - 1) * NumCols(); 1072 int32_t endX = startX + NumCols(); 1073 for (childX = startX; childX < endX; childX++) { 1074 if (!CanChildResize(aVertical, aLeft, childX)) { 1075 return false; 1076 } 1077 } 1078 } 1079 return true; 1080 } 1081 1082 bool nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame) { 1083 nsIContent* content = aChildFrame->GetContent(); 1084 1085 return content && content->IsElement() && 1086 content->AsElement()->HasAttr(nsGkAtoms::noresize); 1087 } 1088 1089 bool nsHTMLFramesetFrame::CanChildResize(bool aVertical, bool aLeft, 1090 int32_t aChildX) { 1091 nsIFrame* child = mFrames.FrameAt(aChildX); 1092 nsHTMLFramesetFrame* frameset = do_QueryFrame(child); 1093 return frameset ? frameset->CanResize(aVertical, aLeft) : !GetNoResize(child); 1094 } 1095 1096 // This calculates and sets the resizability of all border frames 1097 1098 void nsHTMLFramesetFrame::RecalculateBorderResize() { 1099 if (!mContent) { 1100 return; 1101 } 1102 1103 static_assert( 1104 NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT, 1105 "Check for overflow"); 1106 static_assert(NS_MAX_FRAMESET_SPEC_COUNT < 1107 UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT, 1108 "Check for overflow"); 1109 // set the visibility and mouse sensitivity of borders 1110 for (int32_t verX = 0; verX < NumCols() - 1; verX++) { 1111 if (mVerBorders[verX]) { 1112 mVerBorders[verX]->mCanResize = true; 1113 SetBorderResize(mVerBorders[verX]); 1114 } 1115 } 1116 for (int32_t horX = 0; horX < NumRows() - 1; horX++) { 1117 if (mHorBorders[horX]) { 1118 mHorBorders[horX]->mCanResize = true; 1119 SetBorderResize(mHorBorders[horX]); 1120 } 1121 } 1122 } 1123 1124 void nsHTMLFramesetFrame::SetBorderResize( 1125 nsHTMLFramesetBorderFrame* aBorderFrame) { 1126 if (aBorderFrame->mVertical) { 1127 for (int32_t rowX = 0; rowX < NumRows(); rowX++) { 1128 int32_t childX = aBorderFrame->mPrevNeighbor + (rowX * NumCols()); 1129 if (!CanChildResize(true, false, childX) || 1130 !CanChildResize(true, true, childX + 1)) { 1131 aBorderFrame->mCanResize = false; 1132 } 1133 } 1134 } else { 1135 int32_t childX = aBorderFrame->mPrevNeighbor * NumCols(); 1136 int32_t endX = childX + NumCols(); 1137 for (; childX < endX; childX++) { 1138 if (!CanChildResize(false, false, childX)) { 1139 aBorderFrame->mCanResize = false; 1140 } 1141 } 1142 endX = endX + NumCols(); 1143 for (; childX < endX; childX++) { 1144 if (!CanChildResize(false, true, childX)) { 1145 aBorderFrame->mCanResize = false; 1146 } 1147 } 1148 } 1149 } 1150 1151 int32_t nsHTMLFramesetFrame::NumRows() const { 1152 return CheckedInt<int32_t>(mRowSizes.Length()).value(); 1153 } 1154 1155 int32_t nsHTMLFramesetFrame::NumCols() const { 1156 return CheckedInt<int32_t>(mColSizes.Length()).value(); 1157 } 1158 1159 void nsHTMLFramesetFrame::StartMouseDrag(nsPresContext* aPresContext, 1160 nsHTMLFramesetBorderFrame* aBorder, 1161 WidgetGUIEvent* aEvent) { 1162 #if 0 1163 int32_t index; 1164 IndexOf(aBorder, index); 1165 NS_ASSERTION((nullptr != aBorder) && (index >= 0), "invalid dragger"); 1166 #endif 1167 1168 PresShell::SetCapturingContent(GetContent(), 1169 CaptureFlags::IgnoreAllowedState); 1170 1171 mDragger = aBorder; 1172 1173 mFirstDragPoint = aEvent->mRefPoint; 1174 1175 // Store the original frame sizes 1176 if (mDragger->mVertical) { 1177 mPrevNeighborOrigSize = mColSizes[mDragger->mPrevNeighbor]; 1178 mNextNeighborOrigSize = mColSizes[mDragger->mNextNeighbor]; 1179 } else { 1180 mPrevNeighborOrigSize = mRowSizes[mDragger->mPrevNeighbor]; 1181 mNextNeighborOrigSize = mRowSizes[mDragger->mNextNeighbor]; 1182 } 1183 1184 gDragInProgress = true; 1185 } 1186 1187 void nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext, 1188 WidgetGUIEvent* aEvent) { 1189 // if the capture ended, reset the drag state 1190 if (PresShell::GetCapturingContent() != GetContent()) { 1191 mDragger = nullptr; 1192 gDragInProgress = false; 1193 return; 1194 } 1195 1196 int32_t change; // measured positive from left-to-right or top-to-bottom 1197 AutoWeakFrame weakFrame(this); 1198 if (mDragger->mVertical) { 1199 change = aPresContext->DevPixelsToAppUnits(aEvent->mRefPoint.x - 1200 mFirstDragPoint.x); 1201 if (change > mNextNeighborOrigSize - mMinDrag) { 1202 change = mNextNeighborOrigSize - mMinDrag; 1203 } else if (change <= mMinDrag - mPrevNeighborOrigSize) { 1204 change = mMinDrag - mPrevNeighborOrigSize; 1205 } 1206 mColSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change; 1207 mColSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change; 1208 1209 if (change != 0) { 1210 // Recompute the specs from the new sizes. 1211 nscoord width = 1212 mRect.width - (NumCols() - 1) * GetBorderWidth(aPresContext, true); 1213 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent); 1214 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!"); 1215 auto colSpecs = ourContent->GetColSpec(); 1216 nsAutoString newColAttr; 1217 GenerateRowCol(aPresContext, width, colSpecs, mColSizes, newColAttr); 1218 // Setting the attr will trigger a reflow 1219 mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::cols, 1220 newColAttr, true); 1221 } 1222 } else { 1223 change = aPresContext->DevPixelsToAppUnits(aEvent->mRefPoint.y - 1224 mFirstDragPoint.y); 1225 if (change > mNextNeighborOrigSize - mMinDrag) { 1226 change = mNextNeighborOrigSize - mMinDrag; 1227 } else if (change <= mMinDrag - mPrevNeighborOrigSize) { 1228 change = mMinDrag - mPrevNeighborOrigSize; 1229 } 1230 mRowSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change; 1231 mRowSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change; 1232 1233 if (change != 0) { 1234 // Recompute the specs from the new sizes. 1235 nscoord height = 1236 mRect.height - (NumRows() - 1) * GetBorderWidth(aPresContext, true); 1237 HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromNode(mContent); 1238 NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!"); 1239 Span<const nsFramesetSpec> rowSpecs = ourContent->GetRowSpec(); 1240 nsAutoString newRowAttr; 1241 GenerateRowCol(aPresContext, height, rowSpecs, mRowSizes, newRowAttr); 1242 // Setting the attr will trigger a reflow 1243 mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::rows, 1244 newRowAttr, true); 1245 } 1246 } 1247 1248 NS_ENSURE_TRUE_VOID(weakFrame.IsAlive()); 1249 if (change != 0) { 1250 mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this); 1251 } 1252 } 1253 1254 void nsHTMLFramesetFrame::EndMouseDrag(nsPresContext* aPresContext) { 1255 PresShell::ReleaseCapturingContent(); 1256 mDragger = nullptr; 1257 gDragInProgress = false; 1258 } 1259 1260 nsIFrame* NS_NewHTMLFramesetFrame(PresShell* aPresShell, 1261 ComputedStyle* aStyle) { 1262 #ifdef DEBUG 1263 const nsStyleDisplay* disp = aStyle->StyleDisplay(); 1264 NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(), 1265 "Framesets should not be positioned and should not float"); 1266 #endif 1267 1268 return new (aPresShell) 1269 nsHTMLFramesetFrame(aStyle, aPresShell->GetPresContext()); 1270 } 1271 1272 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame) 1273 1274 /******************************************************************************* 1275 * nsHTMLFramesetBorderFrame 1276 ******************************************************************************/ 1277 nsHTMLFramesetBorderFrame::nsHTMLFramesetBorderFrame( 1278 ComputedStyle* aStyle, nsPresContext* aPresContext, int32_t aWidth, 1279 bool aVertical, bool aVisibility) 1280 : nsLeafFrame(aStyle, aPresContext, kClassID), 1281 mWidth(aWidth), 1282 mVertical(aVertical), 1283 mVisibility(aVisibility) { 1284 mCanResize = true; 1285 mColor = NO_COLOR; 1286 mPrevNeighbor = 0; 1287 mNextNeighbor = 0; 1288 } 1289 1290 nsHTMLFramesetBorderFrame::~nsHTMLFramesetBorderFrame() { 1291 // printf("nsHTMLFramesetBorderFrame destructor %p \n", this); 1292 } 1293 1294 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame) 1295 1296 void nsHTMLFramesetBorderFrame::SetVisibility(bool aVisibility) { 1297 mVisibility = aVisibility; 1298 } 1299 1300 void nsHTMLFramesetBorderFrame::SetColor(nscolor aColor) { mColor = aColor; } 1301 1302 void nsHTMLFramesetBorderFrame::Reflow(nsPresContext* aPresContext, 1303 ReflowOutput& aDesiredSize, 1304 const ReflowInput& aReflowInput, 1305 nsReflowStatus& aStatus) { 1306 DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBorderFrame"); 1307 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 1308 1309 // Override Reflow(), since we don't want to deal with what our 1310 // computed values are. We just size to our available size. 1311 aDesiredSize.SetSize(aReflowInput.GetWritingMode(), 1312 aReflowInput.AvailableSize()); 1313 aDesiredSize.SetOverflowAreasToDesiredBounds(); 1314 FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay); 1315 } 1316 1317 class nsDisplayFramesetBorder final : public nsPaintedDisplayItem { 1318 public: 1319 nsDisplayFramesetBorder(nsDisplayListBuilder* aBuilder, 1320 nsHTMLFramesetBorderFrame* aFrame) 1321 : nsPaintedDisplayItem(aBuilder, aFrame) { 1322 MOZ_COUNT_CTOR(nsDisplayFramesetBorder); 1323 } 1324 1325 MOZ_COUNTED_DTOR_FINAL(nsDisplayFramesetBorder) 1326 1327 // REVIEW: see old GetFrameForPoint 1328 // Receives events in its bounds 1329 void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1330 HitTestState* aState, nsTArray<nsIFrame*>* aOutFrames) override { 1331 if (ShouldIgnoreForBackfaceHidden(aState)) { 1332 return; 1333 } 1334 1335 aOutFrames->AppendElement(mFrame); 1336 } 1337 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; 1338 NS_DISPLAY_DECL_NAME("FramesetBorder", TYPE_FRAMESET_BORDER) 1339 }; 1340 1341 void nsDisplayFramesetBorder::Paint(nsDisplayListBuilder* aBuilder, 1342 gfxContext* aCtx) { 1343 static_cast<nsHTMLFramesetBorderFrame*>(mFrame)->PaintBorder( 1344 aCtx->GetDrawTarget(), ToReferenceFrame()); 1345 } 1346 1347 void nsHTMLFramesetBorderFrame::BuildDisplayList( 1348 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { 1349 aLists.Content()->AppendNewToTop<nsDisplayFramesetBorder>(aBuilder, this); 1350 } 1351 1352 void nsHTMLFramesetBorderFrame::PaintBorder(DrawTarget* aDrawTarget, 1353 nsPoint aPt) { 1354 nscoord widthInPixels = nsPresContext::AppUnitsToIntCSSPixels(mWidth); 1355 nscoord pixelWidth = nsPresContext::CSSPixelsToAppUnits(1); 1356 1357 if (widthInPixels <= 0) { 1358 return; 1359 } 1360 1361 ColorPattern bgColor(ToDeviceColor(LookAndFeel::Color( 1362 LookAndFeel::ColorID::Window, this, NS_RGB(200, 200, 200)))); 1363 1364 ColorPattern fgColor(ToDeviceColor(LookAndFeel::Color( 1365 LookAndFeel::ColorID::Windowtext, this, NS_RGB(0, 0, 0)))); 1366 1367 ColorPattern hltColor(ToDeviceColor(LookAndFeel::Color( 1368 LookAndFeel::ColorID::Threedhighlight, this, NS_RGB(255, 255, 255)))); 1369 1370 ColorPattern sdwColor(ToDeviceColor(LookAndFeel::Color( 1371 LookAndFeel::ColorID::Threedshadow, this, NS_RGB(128, 128, 128)))); 1372 1373 ColorPattern color(ToDeviceColor(NS_RGB(255, 255, 255))); // default to white 1374 if (mVisibility) { 1375 color = 1376 (NO_COLOR == mColor) ? bgColor : ColorPattern(ToDeviceColor(mColor)); 1377 } 1378 1379 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); 1380 1381 Point toRefFrame = NSPointToPoint(aPt, appUnitsPerDevPixel); 1382 1383 AutoRestoreTransform autoRestoreTransform(aDrawTarget); 1384 aDrawTarget->SetTransform( 1385 aDrawTarget->GetTransform().PreTranslate(toRefFrame)); 1386 1387 nsPoint start(0, 0); 1388 nsPoint end = mVertical ? nsPoint(0, mRect.height) : nsPoint(mRect.width, 0); 1389 1390 // draw grey or white first 1391 for (int i = 0; i < widthInPixels; i++) { 1392 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget, 1393 color); 1394 if (mVertical) { 1395 start.x += pixelWidth; 1396 end.x = start.x; 1397 } else { 1398 start.y += pixelWidth; 1399 end.y = start.y; 1400 } 1401 } 1402 1403 if (!mVisibility) { 1404 return; 1405 } 1406 1407 if (widthInPixels >= 5) { 1408 start.x = (mVertical) ? pixelWidth : 0; 1409 start.y = (mVertical) ? 0 : pixelWidth; 1410 end.x = (mVertical) ? start.x : mRect.width; 1411 end.y = (mVertical) ? mRect.height : start.y; 1412 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget, 1413 hltColor); 1414 } 1415 1416 if (widthInPixels >= 2) { 1417 start.x = (mVertical) ? mRect.width - (2 * pixelWidth) : 0; 1418 start.y = (mVertical) ? 0 : mRect.height - (2 * pixelWidth); 1419 end.x = (mVertical) ? start.x : mRect.width; 1420 end.y = (mVertical) ? mRect.height : start.y; 1421 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget, 1422 sdwColor); 1423 } 1424 1425 if (widthInPixels >= 1) { 1426 start.x = (mVertical) ? mRect.width - pixelWidth : 0; 1427 start.y = (mVertical) ? 0 : mRect.height - pixelWidth; 1428 end.x = (mVertical) ? start.x : mRect.width; 1429 end.y = (mVertical) ? mRect.height : start.y; 1430 StrokeLineWithSnapping(start, end, appUnitsPerDevPixel, *aDrawTarget, 1431 fgColor); 1432 } 1433 } 1434 1435 nsresult nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext, 1436 WidgetGUIEvent* aEvent, 1437 nsEventStatus* aEventStatus) { 1438 NS_ENSURE_ARG_POINTER(aEventStatus); 1439 *aEventStatus = nsEventStatus_eIgnore; 1440 1441 // XXX Mouse setting logic removed. The remaining logic should also move. 1442 if (!mCanResize) { 1443 return NS_OK; 1444 } 1445 1446 if (aEvent->mMessage == eMouseDown && 1447 aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) { 1448 nsHTMLFramesetFrame* parentFrame = do_QueryFrame(GetParent()); 1449 if (parentFrame) { 1450 parentFrame->StartMouseDrag(aPresContext, this, aEvent); 1451 *aEventStatus = nsEventStatus_eConsumeNoDefault; 1452 } 1453 } 1454 return NS_OK; 1455 } 1456 1457 nsIFrame::Cursor nsHTMLFramesetBorderFrame::GetCursor(const nsPoint&) { 1458 auto kind = StyleCursorKind::Default; 1459 if (mCanResize) { 1460 kind = mVertical ? StyleCursorKind::EwResize : StyleCursorKind::NsResize; 1461 } 1462 return Cursor{kind, AllowCustomCursorImage::No}; 1463 } 1464 1465 #ifdef DEBUG_FRAME_DUMP 1466 nsresult nsHTMLFramesetBorderFrame::GetFrameName(nsAString& aResult) const { 1467 return MakeFrameName(u"FramesetBorder"_ns, aResult); 1468 } 1469 #endif 1470 1471 /******************************************************************************* 1472 * nsHTMLFramesetBlankFrame 1473 ******************************************************************************/ 1474 1475 NS_QUERYFRAME_HEAD(nsHTMLFramesetBlankFrame) 1476 NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame) 1477 NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame) 1478 1479 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame) 1480 1481 nsHTMLFramesetBlankFrame::~nsHTMLFramesetBlankFrame() { 1482 // printf("nsHTMLFramesetBlankFrame destructor %p \n", this); 1483 } 1484 1485 void nsHTMLFramesetBlankFrame::Reflow(nsPresContext* aPresContext, 1486 ReflowOutput& aDesiredSize, 1487 const ReflowInput& aReflowInput, 1488 nsReflowStatus& aStatus) { 1489 DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBlankFrame"); 1490 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 1491 1492 // Override Reflow(), since we don't want to deal with what our 1493 // computed values are. We just size to our available size. 1494 aDesiredSize.SetSize(aReflowInput.GetWritingMode(), 1495 aReflowInput.AvailableSize()); 1496 aDesiredSize.SetOverflowAreasToDesiredBounds(); 1497 FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay); 1498 } 1499 1500 class nsDisplayFramesetBlank final : public nsPaintedDisplayItem { 1501 public: 1502 nsDisplayFramesetBlank(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) 1503 : nsPaintedDisplayItem(aBuilder, aFrame) { 1504 MOZ_COUNT_CTOR(nsDisplayFramesetBlank); 1505 } 1506 1507 MOZ_COUNTED_DTOR_FINAL(nsDisplayFramesetBlank) 1508 1509 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override; 1510 NS_DISPLAY_DECL_NAME("FramesetBlank", TYPE_FRAMESET_BLANK) 1511 }; 1512 1513 void nsDisplayFramesetBlank::Paint(nsDisplayListBuilder* aBuilder, 1514 gfxContext* aCtx) { 1515 DrawTarget* drawTarget = aCtx->GetDrawTarget(); 1516 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); 1517 Rect rect = NSRectToSnappedRect(GetPaintRect(aBuilder, aCtx), 1518 appUnitsPerDevPixel, *drawTarget); 1519 ColorPattern white(ToDeviceColor(sRGBColor::OpaqueWhite())); 1520 drawTarget->FillRect(rect, white); 1521 } 1522 1523 void nsHTMLFramesetBlankFrame::BuildDisplayList( 1524 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { 1525 aLists.Content()->AppendNewToTop<nsDisplayFramesetBlank>(aBuilder, this); 1526 }