layout/generic/nsFrameSetFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 /* rendering object for HTML <frameset> elements */
     8 #include "nsFrameSetFrame.h"
    10 #include "mozilla/DebugOnly.h"
    11 #include "mozilla/Likely.h"
    13 #include "nsGenericHTMLElement.h"
    14 #include "nsAttrValueInlines.h"
    15 #include "nsLeafFrame.h"
    16 #include "nsContainerFrame.h"
    17 #include "nsPresContext.h"
    18 #include "nsIPresShell.h"
    19 #include "nsGkAtoms.h"
    20 #include "nsStyleConsts.h"
    21 #include "nsStyleContext.h"
    22 #include "nsHTMLParts.h"
    23 #include "nsRenderingContext.h"
    24 #include "nsIDOMMutationEvent.h"
    25 #include "nsNameSpaceManager.h"
    26 #include "nsCSSAnonBoxes.h"
    27 #include "nsAutoPtr.h"
    28 #include "nsStyleSet.h"
    29 #include "mozilla/dom/Element.h"
    30 #include "nsDisplayList.h"
    31 #include "nsNodeUtils.h"
    32 #include "mozAutoDocUpdate.h"
    33 #include "mozilla/Preferences.h"
    34 #include "mozilla/dom/HTMLFrameSetElement.h"
    35 #include "mozilla/LookAndFeel.h"
    36 #include "mozilla/MouseEvents.h"
    37 #include "nsSubDocumentFrame.h"
    39 using namespace mozilla;
    40 using namespace mozilla::dom;
    42 // masks for mEdgeVisibility
    43 #define LEFT_VIS   0x0001
    44 #define RIGHT_VIS  0x0002
    45 #define TOP_VIS    0x0004
    46 #define BOTTOM_VIS 0x0008
    47 #define ALL_VIS    0x000F
    48 #define NONE_VIS   0x0000
    50 /*******************************************************************************
    51  * nsFramesetDrag
    52  ******************************************************************************/
    53 nsFramesetDrag::nsFramesetDrag()
    54 {
    55   UnSet();
    56 }
    58 void nsFramesetDrag::Reset(bool                 aVertical,
    59                            int32_t              aIndex,
    60                            int32_t              aChange,
    61                            nsHTMLFramesetFrame* aSource)
    62 {
    63   mVertical = aVertical;
    64   mIndex    = aIndex;
    65   mChange   = aChange;
    66   mSource   = aSource;
    67 }
    69 void nsFramesetDrag::UnSet()
    70 {
    71   mVertical = true;
    72   mIndex    = -1;
    73   mChange   = 0;
    74   mSource   = nullptr;
    75 }
    77 /*******************************************************************************
    78  * nsHTMLFramesetBorderFrame
    79  ******************************************************************************/
    80 class nsHTMLFramesetBorderFrame : public nsLeafFrame
    81 {
    82 public:
    83   NS_DECL_FRAMEARENA_HELPERS
    85 #ifdef DEBUG_FRAME_DUMP
    86   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
    87 #endif
    89   virtual nsresult HandleEvent(nsPresContext* aPresContext,
    90                                WidgetGUIEvent* aEvent,
    91                                nsEventStatus* aEventStatus) MOZ_OVERRIDE;
    93   virtual nsresult GetCursor(const nsPoint&    aPoint,
    94                              nsIFrame::Cursor& aCursor) MOZ_OVERRIDE;
    96   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    97                                 const nsRect&           aDirtyRect,
    98                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
   100   virtual nsresult Reflow(nsPresContext*           aPresContext,
   101                           nsHTMLReflowMetrics&     aDesiredSize,
   102                           const nsHTMLReflowState& aReflowState,
   103                           nsReflowStatus&          aStatus) MOZ_OVERRIDE;
   105   bool GetVisibility() { return mVisibility || mVisibilityOverride; }
   106   void SetVisibility(bool aVisibility);
   107   void SetColor(nscolor aColor);
   109   void PaintBorder(nsRenderingContext& aRenderingContext, nsPoint aPt);
   111 protected:
   112   nsHTMLFramesetBorderFrame(nsStyleContext* aContext, int32_t aWidth, bool aVertical, bool aVisible);
   113   virtual ~nsHTMLFramesetBorderFrame();
   114   virtual nscoord GetIntrinsicWidth() MOZ_OVERRIDE;
   115   virtual nscoord GetIntrinsicHeight() MOZ_OVERRIDE;
   117   // the prev and next neighbors are indexes into the row (for a horizontal border) or col (for
   118   // a vertical border) of nsHTMLFramesetFrames or nsHTMLFrames
   119   int32_t mPrevNeighbor;
   120   int32_t mNextNeighbor;
   121   nscolor mColor;
   122   int32_t mWidth;
   123   bool mVertical;
   124   bool mVisibility;
   125   bool mVisibilityOverride;
   126   bool mCanResize;
   127   friend class nsHTMLFramesetFrame;
   128 };
   129 /*******************************************************************************
   130  * nsHTMLFramesetBlankFrame
   131  ******************************************************************************/
   132 class nsHTMLFramesetBlankFrame : public nsLeafFrame
   133 {
   134 public:
   135   NS_DECL_QUERYFRAME_TARGET(nsHTMLFramesetBlankFrame)
   136   NS_DECL_QUERYFRAME
   137   NS_DECL_FRAMEARENA_HELPERS
   139 #ifdef DEBUG_FRAME_DUMP
   140   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE
   141   {
   142     return MakeFrameName(NS_LITERAL_STRING("FramesetBlank"), aResult);
   143   }
   144 #endif
   146   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   147                                 const nsRect&           aDirtyRect,
   148                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
   150   virtual nsresult Reflow(nsPresContext*           aPresContext,
   151                           nsHTMLReflowMetrics&     aDesiredSize,
   152                           const nsHTMLReflowState& aReflowState,
   153                           nsReflowStatus&          aStatus) MOZ_OVERRIDE;
   155 protected:
   156   nsHTMLFramesetBlankFrame(nsStyleContext* aContext) : nsLeafFrame(aContext) {}
   157   virtual ~nsHTMLFramesetBlankFrame();
   158   virtual nscoord GetIntrinsicWidth() MOZ_OVERRIDE;
   159   virtual nscoord GetIntrinsicHeight() MOZ_OVERRIDE;
   161   friend class nsHTMLFramesetFrame;
   162   friend class nsHTMLFrameset;
   163 };
   165 /*******************************************************************************
   166  * nsHTMLFramesetFrame
   167  ******************************************************************************/
   168 bool    nsHTMLFramesetFrame::gDragInProgress = false;
   169 #define kFrameResizePref "layout.frames.force_resizability"
   170 #define DEFAULT_BORDER_WIDTH_PX 6
   172 nsHTMLFramesetFrame::nsHTMLFramesetFrame(nsStyleContext* aContext)
   173   : nsContainerFrame(aContext)
   174 {
   175   mNumRows             = 0;
   176   mRowSizes            = nullptr;
   177   mNumCols             = 0;
   178   mColSizes            = nullptr;
   179   mEdgeVisibility      = 0;
   180   mParentFrameborder   = eFrameborder_Yes; // default
   181   mParentBorderWidth   = -1; // default not set
   182   mParentBorderColor   = NO_COLOR; // default not set
   183   mFirstDragPoint.x     = mFirstDragPoint.y = 0;
   184   mMinDrag             = nsPresContext::CSSPixelsToAppUnits(2);
   185   mNonBorderChildCount = 0;
   186   mNonBlankChildCount  = 0;
   187   mDragger             = nullptr;
   188   mChildCount          = 0;
   189   mTopLevelFrameset    = nullptr;
   190   mEdgeColors.Set(NO_COLOR);
   191   mVerBorders          = nullptr;
   192   mHorBorders          = nullptr;
   193   mChildFrameborder    = nullptr;
   194   mChildBorderColors   = nullptr;
   195   mForceFrameResizability = false;
   196 }
   198 nsHTMLFramesetFrame::~nsHTMLFramesetFrame()
   199 {
   200   delete[] mRowSizes;
   201   delete[] mColSizes;
   202   delete[] mVerBorders;
   203   delete[] mHorBorders;
   204   delete[] mChildFrameborder;
   205   delete[] mChildBorderColors;
   207   Preferences::UnregisterCallback(FrameResizePrefCallback,
   208                                   kFrameResizePref, this);
   209 }
   211 NS_QUERYFRAME_HEAD(nsHTMLFramesetFrame)
   212   NS_QUERYFRAME_ENTRY(nsHTMLFramesetFrame)
   213 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
   215 // static
   216 void
   217 nsHTMLFramesetFrame::FrameResizePrefCallback(const char* aPref, void* aClosure)
   218 {
   219   nsHTMLFramesetFrame *frame =
   220     reinterpret_cast<nsHTMLFramesetFrame *>(aClosure);
   222   nsIDocument* doc = frame->mContent->GetDocument();
   223   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
   224   if (doc) {
   225     nsNodeUtils::AttributeWillChange(frame->GetContent()->AsElement(),
   226                                      kNameSpaceID_None,
   227                                      nsGkAtoms::frameborder,
   228                                      nsIDOMMutationEvent::MODIFICATION);
   229   }
   231   frame->mForceFrameResizability =
   232     Preferences::GetBool(kFrameResizePref, frame->mForceFrameResizability);
   234   frame->RecalculateBorderResize();
   235   if (doc) {
   236     nsNodeUtils::AttributeChanged(frame->GetContent()->AsElement(),
   237                                   kNameSpaceID_None,
   238                                   nsGkAtoms::frameborder,
   239                                   nsIDOMMutationEvent::MODIFICATION);
   240   }
   241 }
   243 void
   244 nsHTMLFramesetFrame::Init(nsIContent*      aContent,
   245                           nsIFrame*        aParent,
   246                           nsIFrame*        aPrevInFlow)
   247 {
   248   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
   249   // find the highest ancestor that is a frameset
   250   nsIFrame* parentFrame = GetParent();
   251   mTopLevelFrameset = this;
   252   while (parentFrame) {
   253     nsHTMLFramesetFrame* frameset = do_QueryFrame(parentFrame);
   254     if (frameset) {
   255       mTopLevelFrameset = frameset;
   256       parentFrame = parentFrame->GetParent();
   257     } else {
   258       break;
   259     }
   260   }
   262   nsPresContext* presContext = PresContext();
   263   nsIPresShell* shell = presContext->PresShell();
   265   nsFrameborder  frameborder = GetFrameBorder();
   266   int32_t borderWidth = GetBorderWidth(presContext, false);
   267   nscolor borderColor = GetBorderColor();
   269   // Get the rows= cols= data
   270   HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
   271   NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
   272   const nsFramesetSpec* rowSpecs = nullptr;
   273   const nsFramesetSpec* colSpecs = nullptr;
   274   // GetRowSpec and GetColSpec can fail, but when they do they set
   275   // mNumRows and mNumCols respectively to 0, so we deal with it fine.
   276   ourContent->GetRowSpec(&mNumRows, &rowSpecs);
   277   ourContent->GetColSpec(&mNumCols, &colSpecs);
   279   // Maximum value of mNumRows and mNumCols is NS_MAX_FRAMESET_SPEC_COUNT
   280   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscoord));
   281   mRowSizes  = new nscoord[mNumRows];
   282   mColSizes  = new nscoord[mNumCols];
   284   // Ensure we can't overflow numCells
   285   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT);
   286   int32_t numCells = mNumRows*mNumCols;
   288   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nsHTMLFramesetBorderFrame*));
   289   mVerBorders    = new nsHTMLFramesetBorderFrame*[mNumCols];  // 1 more than number of ver borders
   291   for (int verX  = 0; verX < mNumCols; verX++)
   292     mVerBorders[verX]    = nullptr;
   294   mHorBorders    = new nsHTMLFramesetBorderFrame*[mNumRows];  // 1 more than number of hor borders
   296   for (int horX = 0; horX < mNumRows; horX++)
   297     mHorBorders[horX]    = nullptr;
   299   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
   300                    < UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT);
   301   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
   302                    < UINT_MAX / sizeof(nsFrameborder) / NS_MAX_FRAMESET_SPEC_COUNT);
   303   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
   304                    < UINT_MAX / sizeof(nsBorderColor) / NS_MAX_FRAMESET_SPEC_COUNT);
   305   mChildFrameborder  = new nsFrameborder[numCells];
   306   mChildBorderColors  = new nsBorderColor[numCells];
   308   // create the children frames; skip content which isn't <frameset> or <frame>
   309   mChildCount = 0; // number of <frame> or <frameset> children
   310   nsIFrame* frame;
   312   // number of any type of children
   313   uint32_t numChildren = mContent->GetChildCount();
   315   for (uint32_t childX = 0; childX < numChildren; childX++) {
   316     if (mChildCount == numCells) { // we have more <frame> or <frameset> than cells
   317       // Clear the lazy bits in the remaining children.  Also clear
   318       // the restyle flags, like nsCSSFrameConstructor::ProcessChildren does.
   319       for (uint32_t i = childX; i < numChildren; i++) {
   320         nsIContent *child = mContent->GetChildAt(i);
   321         child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
   322         if (child->IsElement()) {
   323           child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
   324         }
   325       }
   326       break;
   327     }
   328     nsIContent *child = mContent->GetChildAt(childX);
   329     child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
   330     // Also clear the restyle flags in the child like
   331     // nsCSSFrameConstructor::ProcessChildren does.
   332     if (child->IsElement()) {
   333       child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
   334     }
   336     // IMPORTANT: This must match the conditions in
   337     // nsCSSFrameConstructor::ContentAppended/Inserted/Removed
   338     if (!child->IsHTML())
   339       continue;
   341     nsIAtom *tag = child->Tag();
   342     if (tag == nsGkAtoms::frameset || tag == nsGkAtoms::frame) {
   343       nsRefPtr<nsStyleContext> kidSC;
   345       kidSC = shell->StyleSet()->ResolveStyleFor(child->AsElement(),
   346                                                  mStyleContext);
   347       if (tag == nsGkAtoms::frameset) {
   348         frame = NS_NewHTMLFramesetFrame(shell, kidSC);
   350         nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame;
   351         childFrame->SetParentFrameborder(frameborder);
   352         childFrame->SetParentBorderWidth(borderWidth);
   353         childFrame->SetParentBorderColor(borderColor);
   354         frame->Init(child, this, nullptr);
   356         mChildBorderColors[mChildCount].Set(childFrame->GetBorderColor());
   357       } else { // frame
   358         frame = NS_NewSubDocumentFrame(shell, kidSC);
   360         frame->Init(child, this, nullptr);
   362         mChildFrameborder[mChildCount] = GetFrameBorder(child);
   363         mChildBorderColors[mChildCount].Set(GetBorderColor(child));
   364       }
   365       child->SetPrimaryFrame(frame);
   367       mFrames.AppendFrame(nullptr, frame);
   369       mChildCount++;
   370     }
   371   }
   373   mNonBlankChildCount = mChildCount;
   374   // add blank frames for frameset cells that had no content provided
   375   for (int blankX = mChildCount; blankX < numCells; blankX++) {
   376     nsRefPtr<nsStyleContext> pseudoStyleContext;
   377     pseudoStyleContext = shell->StyleSet()->
   378       ResolveAnonymousBoxStyle(nsCSSAnonBoxes::framesetBlank, mStyleContext);
   380     // XXX the blank frame is using the content of its parent - at some point it
   381     // should just have null content, if we support that
   382     nsHTMLFramesetBlankFrame* blankFrame = new (shell) nsHTMLFramesetBlankFrame(pseudoStyleContext);
   384     blankFrame->Init(mContent, this, nullptr);
   386     mFrames.AppendFrame(nullptr, blankFrame);
   388     mChildBorderColors[mChildCount].Set(NO_COLOR);
   389     mChildCount++;
   390   }
   392   mNonBorderChildCount = mChildCount;
   393 }
   395 nsresult
   396 nsHTMLFramesetFrame::SetInitialChildList(ChildListID  aListID,
   397                                          nsFrameList& aChildList)
   398 {
   399   // We do this weirdness where we create our child frames in Init().  On the
   400   // other hand, we're going to get a SetInitialChildList() with an empty list
   401   // and null list name after the frame constructor is done creating us.  So
   402   // just ignore that call.
   403   if (aListID == kPrincipalList && aChildList.IsEmpty()) {
   404     return NS_OK;
   405   }
   407   return nsContainerFrame::SetInitialChildList(aListID, aChildList);
   408 }
   410 // XXX should this try to allocate twips based on an even pixel boundary?
   411 void nsHTMLFramesetFrame::Scale(nscoord  aDesired,
   412                                 int32_t  aNumIndicies,
   413                                 int32_t* aIndicies,
   414                                 int32_t  aNumItems,
   415                                 int32_t* aItems)
   416 {
   417   int32_t actual = 0;
   418   int32_t i, j;
   419   // get the actual total
   420   for (i = 0; i < aNumIndicies; i++) {
   421     j = aIndicies[i];
   422     actual += aItems[j];
   423   }
   425   if (actual > 0) {
   426     float factor = (float)aDesired / (float)actual;
   427     actual = 0;
   428     // scale the items up or down
   429     for (i = 0; i < aNumIndicies; i++) {
   430       j = aIndicies[i];
   431       aItems[j] = NSToCoordRound((float)aItems[j] * factor);
   432       actual += aItems[j];
   433     }
   434   } else if (aNumIndicies != 0) {
   435     // All the specs say zero width, but we have to fill up space
   436     // somehow.  Distribute it equally.
   437     nscoord width = NSToCoordRound((float)aDesired / (float)aNumIndicies);
   438     actual = width * aNumIndicies;
   439     for (i = 0; i < aNumIndicies; i++) {
   440       aItems[aIndicies[i]] = width;
   441     }
   442   }
   444   if (aNumIndicies > 0 && aDesired != actual) {
   445     int32_t unit = (aDesired > actual) ? 1 : -1;
   446     for (i=0; (i < aNumIndicies) && (aDesired != actual); i++) {
   447       j = aIndicies[i];
   448       if (j < aNumItems) {
   449         aItems[j] += unit;
   450         actual += unit;
   451       }
   452     }
   453   }
   454 }
   457 /**
   458   * Translate the rows/cols specs into an array of integer sizes for
   459   * each cell in the frameset. Sizes are allocated based on the priorities of the
   460   * specifier - fixed sizes have the highest priority, percentage sizes have the next
   461   * highest priority and relative sizes have the lowest.
   462   */
   463 void nsHTMLFramesetFrame::CalculateRowCol(nsPresContext*        aPresContext,
   464                                           nscoord               aSize,
   465                                           int32_t               aNumSpecs,
   466                                           const nsFramesetSpec* aSpecs,
   467                                           nscoord*              aValues)
   468 {
   469   // aNumSpecs maximum value is NS_MAX_FRAMESET_SPEC_COUNT
   470   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(int32_t));
   472   int32_t  fixedTotal = 0;
   473   int32_t  numFixed = 0;
   474   nsAutoArrayPtr<int32_t> fixed(new int32_t[aNumSpecs]);
   475   int32_t  numPercent = 0;
   476   nsAutoArrayPtr<int32_t> percent(new int32_t[aNumSpecs]);
   477   int32_t  relativeSums = 0;
   478   int32_t  numRelative = 0;
   479   nsAutoArrayPtr<int32_t> relative(new int32_t[aNumSpecs]);
   481   if (MOZ_UNLIKELY(!fixed || !percent || !relative)) {
   482     return; // NS_ERROR_OUT_OF_MEMORY
   483   }
   485   int32_t i, j;
   487   // initialize the fixed, percent, relative indices, allocate the fixed sizes and zero the others
   488   for (i = 0; i < aNumSpecs; i++) {
   489     aValues[i] = 0;
   490     switch (aSpecs[i].mUnit) {
   491       case eFramesetUnit_Fixed:
   492         aValues[i] = nsPresContext::CSSPixelsToAppUnits(aSpecs[i].mValue);
   493         fixedTotal += aValues[i];
   494         fixed[numFixed] = i;
   495         numFixed++;
   496         break;
   497       case eFramesetUnit_Percent:
   498         percent[numPercent] = i;
   499         numPercent++;
   500         break;
   501       case eFramesetUnit_Relative:
   502         relative[numRelative] = i;
   503         numRelative++;
   504         relativeSums += aSpecs[i].mValue;
   505         break;
   506     }
   507   }
   509   // scale the fixed sizes if they total too much (or too little and there aren't any percent or relative)
   510   if ((fixedTotal > aSize) || ((fixedTotal < aSize) && (0 == numPercent) && (0 == numRelative))) {
   511     Scale(aSize, numFixed, fixed, aNumSpecs, aValues);
   512     return;
   513   }
   515   int32_t percentMax = aSize - fixedTotal;
   516   int32_t percentTotal = 0;
   517   // allocate the percentage sizes from what is left over from the fixed allocation
   518   for (i = 0; i < numPercent; i++) {
   519     j = percent[i];
   520     aValues[j] = NSToCoordRound((float)aSpecs[j].mValue * (float)aSize / 100.0f);
   521     percentTotal += aValues[j];
   522   }
   524   // scale the percent sizes if they total too much (or too little and there aren't any relative)
   525   if ((percentTotal > percentMax) || ((percentTotal < percentMax) && (0 == numRelative))) {
   526     Scale(percentMax, numPercent, percent, aNumSpecs, aValues);
   527     return;
   528   }
   530   int32_t relativeMax = percentMax - percentTotal;
   531   int32_t relativeTotal = 0;
   532   // allocate the relative sizes from what is left over from the percent allocation
   533   for (i = 0; i < numRelative; i++) {
   534     j = relative[i];
   535     aValues[j] = NSToCoordRound((float)aSpecs[j].mValue * (float)relativeMax / (float)relativeSums);
   536     relativeTotal += aValues[j];
   537   }
   539   // scale the relative sizes if they take up too much or too little
   540   if (relativeTotal != relativeMax) {
   541     Scale(relativeMax, numRelative, relative, aNumSpecs, aValues);
   542   }
   543 }
   546 /**
   547   * Translate the rows/cols integer sizes into an array of specs for
   548   * each cell in the frameset.  Reverse of CalculateRowCol() behaviour.
   549   * This allows us to maintain the user size info through reflows.
   550   */
   551 void nsHTMLFramesetFrame::GenerateRowCol(nsPresContext*        aPresContext,
   552                                          nscoord               aSize,
   553                                          int32_t               aNumSpecs,
   554                                          const nsFramesetSpec* aSpecs,
   555                                          nscoord*              aValues,
   556                                          nsString&             aNewAttr)
   557 {
   558   int32_t i;
   560   for (i = 0; i < aNumSpecs; i++) {
   561     if (!aNewAttr.IsEmpty())
   562       aNewAttr.Append(char16_t(','));
   564     switch (aSpecs[i].mUnit) {
   565       case eFramesetUnit_Fixed:
   566         aNewAttr.AppendInt(nsPresContext::AppUnitsToIntCSSPixels(aValues[i]));
   567         break;
   568       case eFramesetUnit_Percent: // XXX Only accurate to 1%, need 1 pixel
   569       case eFramesetUnit_Relative:
   570         // Add 0.5 to the percentage to make rounding work right.
   571         aNewAttr.AppendInt(uint32_t((100.0*aValues[i])/aSize + 0.5));
   572         aNewAttr.Append(char16_t('%'));
   573         break;
   574     }
   575   }
   576 }
   578 int32_t nsHTMLFramesetFrame::GetBorderWidth(nsPresContext* aPresContext,
   579                                             bool aTakeForcingIntoAccount)
   580 {
   581   bool forcing = mForceFrameResizability && aTakeForcingIntoAccount;
   583   if (!forcing) {
   584     nsFrameborder frameborder = GetFrameBorder();
   585     if (frameborder == eFrameborder_No) {
   586       return 0;
   587     }
   588   }
   589   nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
   591   if (content) {
   592     const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::border);
   593     if (attr) {
   594       int32_t intVal = 0;
   595       if (attr->Type() == nsAttrValue::eInteger) {
   596         intVal = attr->GetIntegerValue();
   597         if (intVal < 0) {
   598           intVal = 0;
   599         }
   600       }
   602       if (forcing && intVal == 0) {
   603         intVal = DEFAULT_BORDER_WIDTH_PX;
   604       }
   605       return nsPresContext::CSSPixelsToAppUnits(intVal);
   606     }
   607   }
   609   if (mParentBorderWidth > 0 ||
   610       (mParentBorderWidth == 0 && !forcing)) {
   611     return mParentBorderWidth;
   612   }
   614   return nsPresContext::CSSPixelsToAppUnits(DEFAULT_BORDER_WIDTH_PX);
   615 }
   617 void
   618 nsHTMLFramesetFrame::GetDesiredSize(nsPresContext*           aPresContext,
   619                                     const nsHTMLReflowState& aReflowState,
   620                                     nsHTMLReflowMetrics&     aDesiredSize)
   621 {
   622   nsHTMLFramesetFrame* framesetParent = do_QueryFrame(GetParent());
   623   if (nullptr == framesetParent) {
   624     if (aPresContext->IsPaginated()) {
   625       // XXX This needs to be changed when framesets paginate properly
   626       aDesiredSize.Width() = aReflowState.AvailableWidth();
   627       aDesiredSize.Height() = aReflowState.AvailableHeight();
   628     } else {
   629       nsRect area = aPresContext->GetVisibleArea();
   631       aDesiredSize.Width() = area.width;
   632       aDesiredSize.Height() = area.height;
   633     }
   634   } else {
   635     nsSize size;
   636     framesetParent->GetSizeOfChild(this, size);
   637     aDesiredSize.Width() = size.width;
   638     aDesiredSize.Height() = size.height;
   639   }
   640 }
   642 // only valid for non border children
   643 void nsHTMLFramesetFrame::GetSizeOfChildAt(int32_t  aIndexInParent,
   644                                            nsSize&  aSize,
   645                                            nsIntPoint& aCellIndex)
   646 {
   647   int32_t row = aIndexInParent / mNumCols;
   648   int32_t col = aIndexInParent - (row * mNumCols); // remainder from dividing index by mNumCols
   649   if ((row < mNumRows) && (col < mNumCols)) {
   650     aSize.width  = mColSizes[col];
   651     aSize.height = mRowSizes[row];
   652     aCellIndex.x = col;
   653     aCellIndex.y = row;
   654   } else {
   655     aSize.width = aSize.height = 0;
   656     aCellIndex.x = aCellIndex.y = 0;
   657   }
   658 }
   660 // only valid for non border children
   661 void nsHTMLFramesetFrame::GetSizeOfChild(nsIFrame* aChild,
   662                                          nsSize&   aSize)
   663 {
   664   // Reflow only creates children frames for <frameset> and <frame> content.
   665   // this assumption is used here
   666   int i = 0;
   667   for (nsIFrame* child = mFrames.FirstChild(); child;
   668        child = child->GetNextSibling()) {
   669     if (aChild == child) {
   670       nsIntPoint ignore;
   671       GetSizeOfChildAt(i, aSize, ignore);
   672       return;
   673     }
   674     i++;
   675   }
   676   aSize.width  = 0;
   677   aSize.height = 0;
   678 }
   681 nsresult nsHTMLFramesetFrame::HandleEvent(nsPresContext* aPresContext,
   682                                            WidgetGUIEvent* aEvent,
   683                                            nsEventStatus* aEventStatus)
   684 {
   685   NS_ENSURE_ARG_POINTER(aEventStatus);
   686   if (mDragger) {
   687     // the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
   688     switch (aEvent->message) {
   689       case NS_MOUSE_MOVE:
   690         MouseDrag(aPresContext, aEvent);
   691 	      break;
   692       case NS_MOUSE_BUTTON_UP:
   693         if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
   694           EndMouseDrag(aPresContext);
   695         }
   696 	      break;
   697     }
   698     *aEventStatus = nsEventStatus_eConsumeNoDefault;
   699   } else {
   700     *aEventStatus = nsEventStatus_eIgnore;
   701   }
   702   return NS_OK;
   703 }
   705 nsresult
   706 nsHTMLFramesetFrame::GetCursor(const nsPoint&    aPoint,
   707                                nsIFrame::Cursor& aCursor)
   708 {
   709   if (mDragger) {
   710     aCursor.mCursor = (mDragger->mVertical) ? NS_STYLE_CURSOR_EW_RESIZE : NS_STYLE_CURSOR_NS_RESIZE;
   711   } else {
   712     aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
   713   }
   714   return NS_OK;
   715 }
   717 void
   718 nsHTMLFramesetFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   719                                       const nsRect&           aDirtyRect,
   720                                       const nsDisplayListSet& aLists)
   721 {
   722   BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
   724   if (mDragger && aBuilder->IsForEventDelivery()) {
   725     aLists.Content()->AppendNewToTop(
   726       new (aBuilder) nsDisplayEventReceiver(aBuilder, this));
   727   }
   728 }
   730 void
   731 nsHTMLFramesetFrame::ReflowPlaceChild(nsIFrame*                aChild,
   732                                       nsPresContext*           aPresContext,
   733                                       const nsHTMLReflowState& aReflowState,
   734                                       nsPoint&                 aOffset,
   735                                       nsSize&                  aSize,
   736                                       nsIntPoint*              aCellIndex)
   737 {
   738   // reflow the child
   739   nsHTMLReflowState reflowState(aPresContext, aReflowState, aChild, aSize);
   740   reflowState.SetComputedWidth(std::max(0, aSize.width - reflowState.ComputedPhysicalBorderPadding().LeftRight()));
   741   reflowState.SetComputedHeight(std::max(0, aSize.height - reflowState.ComputedPhysicalBorderPadding().TopBottom()));
   742   nsHTMLReflowMetrics metrics(aReflowState);
   743   metrics.Width() = aSize.width;
   744   metrics.Height() = aSize.height;
   745   nsReflowStatus status;
   747   ReflowChild(aChild, aPresContext, metrics, reflowState, aOffset.x,
   748               aOffset.y, 0, status);
   749   NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
   751   // Place and size the child
   752   metrics.Width() = aSize.width;
   753   metrics.Height() = aSize.height;
   754   FinishReflowChild(aChild, aPresContext, metrics, nullptr, aOffset.x, aOffset.y, 0);
   755 }
   757 static
   758 nsFrameborder GetFrameBorderHelper(nsGenericHTMLElement* aContent)
   759 {
   760   if (nullptr != aContent) {
   761     const nsAttrValue* attr = aContent->GetParsedAttr(nsGkAtoms::frameborder);
   762     if (attr && attr->Type() == nsAttrValue::eEnum) {
   763       switch (attr->GetEnumValue())
   764       {
   765         case NS_STYLE_FRAME_YES:
   766         case NS_STYLE_FRAME_1:
   767           return eFrameborder_Yes;
   768           break;
   770         case NS_STYLE_FRAME_NO:
   771         case NS_STYLE_FRAME_0:
   772           return eFrameborder_No;
   773           break;
   774       }
   775     }
   776   }
   777   return eFrameborder_Notset;
   778 }
   780 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder()
   781 {
   782   nsFrameborder result = eFrameborder_Notset;
   783   nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
   785   if (content) {
   786     result = GetFrameBorderHelper(content);
   787   }
   788   if (eFrameborder_Notset == result) {
   789     return mParentFrameborder;
   790   }
   791   return result;
   792 }
   794 nsFrameborder nsHTMLFramesetFrame::GetFrameBorder(nsIContent* aContent)
   795 {
   796   nsFrameborder result = eFrameborder_Notset;
   798   nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(aContent);
   800   if (content) {
   801     result = GetFrameBorderHelper(content);
   802   }
   803   if (eFrameborder_Notset == result) {
   804     return GetFrameBorder();
   805   }
   806   return result;
   807 }
   809 nscolor nsHTMLFramesetFrame::GetBorderColor()
   810 {
   811   nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
   813   if (content) {
   814     const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor);
   815     if (attr) {
   816       nscolor color;
   817       if (attr->GetColorValue(color)) {
   818         return color;
   819       }
   820     }
   821   }
   823   return mParentBorderColor;
   824 }
   826 nscolor nsHTMLFramesetFrame::GetBorderColor(nsIContent* aContent)
   827 {
   828   nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(aContent);
   830   if (content) {
   831     const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::bordercolor);
   832     if (attr) {
   833       nscolor color;
   834       if (attr->GetColorValue(color)) {
   835         return color;
   836       }
   837     }
   838   }
   839   return GetBorderColor();
   840 }
   842 nsresult
   843 nsHTMLFramesetFrame::Reflow(nsPresContext*           aPresContext,
   844                             nsHTMLReflowMetrics&     aDesiredSize,
   845                             const nsHTMLReflowState& aReflowState,
   846                             nsReflowStatus&          aStatus)
   847 {
   848   DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame");
   849   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   850   nsIPresShell *shell = aPresContext->PresShell();
   851   nsStyleSet *styleSet = shell->StyleSet();
   853   mParent->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
   855   //printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
   856   // Always get the size so that the caller knows how big we are
   857   GetDesiredSize(aPresContext, aReflowState, aDesiredSize);
   859   nscoord width  = (aDesiredSize.Width() <= aReflowState.AvailableWidth())
   860     ? aDesiredSize.Width() : aReflowState.AvailableWidth();
   861   nscoord height = (aDesiredSize.Height() <= aReflowState.AvailableHeight())
   862     ? aDesiredSize.Height() : aReflowState.AvailableHeight();
   864   bool firstTime = (GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
   865   if (firstTime) {
   866     Preferences::RegisterCallback(FrameResizePrefCallback,
   867                                   kFrameResizePref, this);
   868     mForceFrameResizability = Preferences::GetBool(kFrameResizePref);
   869   }
   871   // subtract out the width of all of the potential borders. There are
   872   // only borders between <frame>s. There are none on the edges (e.g the
   873   // leftmost <frame> has no left border).
   874   int32_t borderWidth = GetBorderWidth(aPresContext, true);
   876   width  -= (mNumCols - 1) * borderWidth;
   877   if (width < 0) width = 0;
   879   height -= (mNumRows - 1) * borderWidth;
   880   if (height < 0) height = 0;
   882   HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
   883   NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
   884   const nsFramesetSpec* rowSpecs = nullptr;
   885   const nsFramesetSpec* colSpecs = nullptr;
   886   int32_t rows = 0;
   887   int32_t cols = 0;
   888   ourContent->GetRowSpec(&rows, &rowSpecs);
   889   ourContent->GetColSpec(&cols, &colSpecs);
   890   // If the number of cols or rows has changed, the frame for the frameset
   891   // will be re-created.
   892   if (mNumRows != rows || mNumCols != cols) {
   893     aStatus = NS_FRAME_COMPLETE;
   894     mDrag.UnSet();
   895     NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   896     return NS_OK;
   897   }
   899   CalculateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes);
   900   CalculateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes);
   902   nsAutoArrayPtr<bool>  verBordersVis; // vertical borders visibility
   903   nsAutoArrayPtr<nscolor> verBorderColors;
   904   nsAutoArrayPtr<bool>  horBordersVis; // horizontal borders visibility
   905   nsAutoArrayPtr<nscolor> horBorderColors;
   906   nscolor                 borderColor = GetBorderColor();
   907   nsFrameborder           frameborder = GetFrameBorder();
   909   if (firstTime) {
   910     // Check for overflow in memory allocations using mNumCols and mNumRows
   911     // which have a maxium value of NS_MAX_FRAMESET_SPEC_COUNT.
   912     PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(bool));
   913     PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < UINT_MAX / sizeof(nscolor));
   915     verBordersVis = new bool[mNumCols];
   916     NS_ENSURE_TRUE(verBordersVis, NS_ERROR_OUT_OF_MEMORY);
   917     verBorderColors = new nscolor[mNumCols];
   918     NS_ENSURE_TRUE(verBorderColors, NS_ERROR_OUT_OF_MEMORY);
   919     for (int verX  = 0; verX < mNumCols; verX++) {
   920       verBordersVis[verX] = false;
   921       verBorderColors[verX] = NO_COLOR;
   922     }
   924     horBordersVis = new bool[mNumRows];
   925     NS_ENSURE_TRUE(horBordersVis, NS_ERROR_OUT_OF_MEMORY);
   926     horBorderColors = new nscolor[mNumRows];
   927     NS_ENSURE_TRUE(horBorderColors, NS_ERROR_OUT_OF_MEMORY);
   928     for (int horX = 0; horX < mNumRows; horX++) {
   929       horBordersVis[horX] = false;
   930       horBorderColors[horX] = NO_COLOR;
   931     }
   932   }
   934   // reflow the children
   935   int32_t lastRow = 0;
   936   int32_t lastCol = 0;
   937   int32_t borderChildX = mNonBorderChildCount; // index of border children
   938   nsHTMLFramesetBorderFrame* borderFrame = nullptr;
   939   nsPoint offset(0,0);
   940   nsSize size, lastSize;
   941   nsIFrame* child = mFrames.FirstChild();
   943   for (int32_t childX = 0; childX < mNonBorderChildCount; childX++) {
   944     nsIntPoint cellIndex;
   945     GetSizeOfChildAt(childX, size, cellIndex);
   947     if (lastRow != cellIndex.y) {  // changed to next row
   948       offset.x = 0;
   949       offset.y += lastSize.height;
   950       if (firstTime) { // create horizontal border
   952         nsRefPtr<nsStyleContext> pseudoStyleContext;
   953         pseudoStyleContext = styleSet->
   954           ResolveAnonymousBoxStyle(nsCSSAnonBoxes::horizontalFramesetBorder,
   955                                    mStyleContext);
   957         borderFrame = new (shell) nsHTMLFramesetBorderFrame(pseudoStyleContext,
   958                                                             borderWidth,
   959                                                             false,
   960                                                             false);
   961         borderFrame->Init(mContent, this, nullptr);
   962         mChildCount++;
   963         mFrames.AppendFrame(nullptr, borderFrame);
   964         mHorBorders[cellIndex.y-1] = borderFrame;
   965         // set the neighbors for determining drag boundaries
   966         borderFrame->mPrevNeighbor = lastRow;
   967         borderFrame->mNextNeighbor = cellIndex.y;
   968       } else {
   969         borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
   970         borderFrame->mWidth = borderWidth;
   971         borderChildX++;
   972       }
   973       nsSize borderSize(aDesiredSize.Width(), borderWidth);
   974       ReflowPlaceChild(borderFrame, aPresContext, aReflowState, offset, borderSize);
   975       borderFrame = nullptr;
   976       offset.y += borderWidth;
   977     } else {
   978       if (cellIndex.x > 0) {  // moved to next col in same row
   979         if (0 == cellIndex.y) { // in 1st row
   980           if (firstTime) { // create vertical border
   982             nsRefPtr<nsStyleContext> pseudoStyleContext;
   983             pseudoStyleContext = styleSet->
   984               ResolveAnonymousBoxStyle(nsCSSAnonBoxes::verticalFramesetBorder,
   985                                        mStyleContext);
   987             borderFrame = new (shell) nsHTMLFramesetBorderFrame(pseudoStyleContext,
   988                                                                 borderWidth,
   989                                                                 true,
   990                                                                 false);
   991             borderFrame->Init(mContent, this, nullptr);
   992             mChildCount++;
   993             mFrames.AppendFrame(nullptr, borderFrame);
   994             mVerBorders[cellIndex.x-1] = borderFrame;
   995             // set the neighbors for determining drag boundaries
   996             borderFrame->mPrevNeighbor = lastCol;
   997             borderFrame->mNextNeighbor = cellIndex.x;
   998           } else {
   999             borderFrame = (nsHTMLFramesetBorderFrame*)mFrames.FrameAt(borderChildX);
  1000             borderFrame->mWidth = borderWidth;
  1001             borderChildX++;
  1003           nsSize borderSize(borderWidth, aDesiredSize.Height());
  1004           ReflowPlaceChild(borderFrame, aPresContext, aReflowState, offset, borderSize);
  1005           borderFrame = nullptr;
  1007         offset.x += borderWidth;
  1011     ReflowPlaceChild(child, aPresContext, aReflowState, offset, size, &cellIndex);
  1013     if (firstTime) {
  1014       int32_t childVis;
  1015       nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(child);
  1016       nsSubDocumentFrame* subdocFrame;
  1017       if (framesetFrame) {
  1018         childVis = framesetFrame->mEdgeVisibility;
  1019         mChildBorderColors[childX] = framesetFrame->mEdgeColors;
  1020       } else if ((subdocFrame = do_QueryFrame(child))) {
  1021         if (eFrameborder_Yes == mChildFrameborder[childX]) {
  1022           childVis = ALL_VIS;
  1023         } else if (eFrameborder_No == mChildFrameborder[childX]) {
  1024           childVis = NONE_VIS;
  1025         } else {  // notset
  1026           childVis = (eFrameborder_No == frameborder) ? NONE_VIS : ALL_VIS;
  1028       } else {  // blank
  1029         DebugOnly<nsHTMLFramesetBlankFrame*> blank;
  1030         MOZ_ASSERT(blank = do_QueryFrame(child), "unexpected child frame type");
  1031         childVis = NONE_VIS;
  1033       nsBorderColor childColors = mChildBorderColors[childX];
  1034       // set the visibility, color of our edge borders based on children
  1035       if (0 == cellIndex.x) {
  1036         if (!(mEdgeVisibility & LEFT_VIS)) {
  1037           mEdgeVisibility |= (LEFT_VIS & childVis);
  1039         if (NO_COLOR == mEdgeColors.mLeft) {
  1040           mEdgeColors.mLeft = childColors.mLeft;
  1043       if (0 == cellIndex.y) {
  1044         if (!(mEdgeVisibility & TOP_VIS)) {
  1045           mEdgeVisibility |= (TOP_VIS & childVis);
  1047         if (NO_COLOR == mEdgeColors.mTop) {
  1048           mEdgeColors.mTop = childColors.mTop;
  1051       if (mNumCols-1 == cellIndex.x) {
  1052         if (!(mEdgeVisibility & RIGHT_VIS)) {
  1053           mEdgeVisibility |= (RIGHT_VIS & childVis);
  1055         if (NO_COLOR == mEdgeColors.mRight) {
  1056           mEdgeColors.mRight = childColors.mRight;
  1059       if (mNumRows-1 == cellIndex.y) {
  1060         if (!(mEdgeVisibility & BOTTOM_VIS)) {
  1061           mEdgeVisibility |= (BOTTOM_VIS & childVis);
  1063         if (NO_COLOR == mEdgeColors.mBottom) {
  1064           mEdgeColors.mBottom = childColors.mBottom;
  1067       // set the visibility of borders that the child may affect
  1068       if (childVis & RIGHT_VIS) {
  1069         verBordersVis[cellIndex.x] = true;
  1071       if (childVis & BOTTOM_VIS) {
  1072         horBordersVis[cellIndex.y] = true;
  1074       if ((cellIndex.x > 0) && (childVis & LEFT_VIS)) {
  1075         verBordersVis[cellIndex.x-1] = true;
  1077       if ((cellIndex.y > 0) && (childVis & TOP_VIS)) {
  1078         horBordersVis[cellIndex.y-1] = true;
  1080       // set the colors of borders that the child may affect
  1081       if (NO_COLOR == verBorderColors[cellIndex.x]) {
  1082         verBorderColors[cellIndex.x] = mChildBorderColors[childX].mRight;
  1084       if (NO_COLOR == horBorderColors[cellIndex.y]) {
  1085         horBorderColors[cellIndex.y] = mChildBorderColors[childX].mBottom;
  1087       if ((cellIndex.x > 0) && (NO_COLOR == verBorderColors[cellIndex.x-1])) {
  1088         verBorderColors[cellIndex.x-1] = mChildBorderColors[childX].mLeft;
  1090       if ((cellIndex.y > 0) && (NO_COLOR == horBorderColors[cellIndex.y-1])) {
  1091         horBorderColors[cellIndex.y-1] = mChildBorderColors[childX].mTop;
  1094     lastRow  = cellIndex.y;
  1095     lastCol  = cellIndex.x;
  1096     lastSize = size;
  1097     offset.x += size.width;
  1098     child = child->GetNextSibling();
  1101   if (firstTime) {
  1102     nscolor childColor;
  1103     // set the visibility, color, mouse sensitivity of borders
  1104     for (int verX = 0; verX < mNumCols-1; verX++) {
  1105       if (mVerBorders[verX]) {
  1106         mVerBorders[verX]->SetVisibility(verBordersVis[verX]);
  1107         if (mForceFrameResizability) {
  1108           mVerBorders[verX]->mVisibilityOverride = true;
  1109         } else {
  1110           SetBorderResize(mVerBorders[verX]);
  1112         childColor = (NO_COLOR == verBorderColors[verX]) ? borderColor : verBorderColors[verX];
  1113         mVerBorders[verX]->SetColor(childColor);
  1116     for (int horX = 0; horX < mNumRows-1; horX++) {
  1117       if (mHorBorders[horX]) {
  1118         mHorBorders[horX]->SetVisibility(horBordersVis[horX]);
  1119         if (mForceFrameResizability) {
  1120           mHorBorders[horX]->mVisibilityOverride = true;
  1121         } else {
  1122           SetBorderResize(mHorBorders[horX]);
  1124         childColor = (NO_COLOR == horBorderColors[horX]) ? borderColor : horBorderColors[horX];
  1125         mHorBorders[horX]->SetColor(childColor);
  1129     delete[] mChildFrameborder;
  1130     delete[] mChildBorderColors;
  1132     mChildFrameborder = nullptr;
  1133     mChildBorderColors = nullptr;
  1136   aStatus = NS_FRAME_COMPLETE;
  1137   mDrag.UnSet();
  1139   aDesiredSize.SetOverflowAreasToDesiredBounds();
  1140   FinishAndStoreOverflow(&aDesiredSize);
  1142   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
  1143   return NS_OK;
  1146 nsIAtom*
  1147 nsHTMLFramesetFrame::GetType() const
  1149   return nsGkAtoms::frameSetFrame;
  1152 #ifdef DEBUG_FRAME_DUMP
  1153 nsresult
  1154 nsHTMLFramesetFrame::GetFrameName(nsAString& aResult) const
  1156   return MakeFrameName(NS_LITERAL_STRING("Frameset"), aResult);
  1158 #endif
  1160 bool
  1161 nsHTMLFramesetFrame::IsLeaf() const
  1163   // We handle constructing our kids manually
  1164   return true;
  1167 bool
  1168 nsHTMLFramesetFrame::CanResize(bool aVertical,
  1169                                bool aLeft)
  1171   int32_t childX;
  1172   int32_t startX;
  1173   if (aVertical) {
  1174     startX = (aLeft) ? 0 : mNumCols-1;
  1175     for (childX = startX; childX < mNonBorderChildCount; childX += mNumCols) {
  1176       if (!CanChildResize(aVertical, aLeft, childX)) {
  1177         return false;
  1180   } else {
  1181     startX = (aLeft) ? 0 : (mNumRows - 1) * mNumCols;
  1182     int32_t endX = startX + mNumCols;
  1183     for (childX = startX; childX < endX; childX++) {
  1184       if (!CanChildResize(aVertical, aLeft, childX)) {
  1185         return false;
  1189   return true;
  1192 bool
  1193 nsHTMLFramesetFrame::GetNoResize(nsIFrame* aChildFrame)
  1195   nsIContent* content = aChildFrame->GetContent();
  1197   return content && content->HasAttr(kNameSpaceID_None, nsGkAtoms::noresize);
  1200 bool
  1201 nsHTMLFramesetFrame::CanChildResize(bool aVertical, bool aLeft, int32_t aChildX)
  1203   nsIFrame* child = mFrames.FrameAt(aChildX);
  1204   nsHTMLFramesetFrame* frameset = do_QueryFrame(child);
  1205   return frameset ? frameset->CanResize(aVertical, aLeft) : !GetNoResize(child);
  1208 // This calculates and sets the resizability of all border frames
  1210 void
  1211 nsHTMLFramesetFrame::RecalculateBorderResize()
  1213   if (!mContent) {
  1214     return;
  1217   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT < INT32_MAX / NS_MAX_FRAMESET_SPEC_COUNT);
  1218   PR_STATIC_ASSERT(NS_MAX_FRAMESET_SPEC_COUNT
  1219                    < UINT_MAX / sizeof(int32_t) / NS_MAX_FRAMESET_SPEC_COUNT);
  1220   // set the visibility and mouse sensitivity of borders
  1221   int32_t verX;
  1222   for (verX = 0; verX < mNumCols-1; verX++) {
  1223     if (mVerBorders[verX]) {
  1224       mVerBorders[verX]->mCanResize = true;
  1225       if (mForceFrameResizability) {
  1226         mVerBorders[verX]->mVisibilityOverride = true;
  1227       } else {
  1228         mVerBorders[verX]->mVisibilityOverride = false;
  1229         SetBorderResize(mVerBorders[verX]);
  1233   int32_t horX;
  1234   for (horX = 0; horX < mNumRows-1; horX++) {
  1235     if (mHorBorders[horX]) {
  1236       mHorBorders[horX]->mCanResize = true;
  1237       if (mForceFrameResizability) {
  1238         mHorBorders[horX]->mVisibilityOverride = true;
  1239       } else {
  1240         mHorBorders[horX]->mVisibilityOverride = false;
  1241         SetBorderResize(mHorBorders[horX]);
  1247 void
  1248 nsHTMLFramesetFrame::SetBorderResize(nsHTMLFramesetBorderFrame* aBorderFrame)
  1250   if (aBorderFrame->mVertical) {
  1251     for (int rowX = 0; rowX < mNumRows; rowX++) {
  1252       int32_t childX = aBorderFrame->mPrevNeighbor + (rowX * mNumCols);
  1253       if (!CanChildResize(true, false, childX) ||
  1254           !CanChildResize(true, true, childX+1)) {
  1255         aBorderFrame->mCanResize = false;
  1258   } else {
  1259     int32_t childX = aBorderFrame->mPrevNeighbor * mNumCols;
  1260     int32_t endX   = childX + mNumCols;
  1261     for (; childX < endX; childX++) {
  1262       if (!CanChildResize(false, false, childX)) {
  1263         aBorderFrame->mCanResize = false;
  1266     endX = endX + mNumCols;
  1267     for (; childX < endX; childX++) {
  1268       if (!CanChildResize(false, true, childX)) {
  1269         aBorderFrame->mCanResize = false;
  1275 void
  1276 nsHTMLFramesetFrame::StartMouseDrag(nsPresContext*             aPresContext,
  1277                                     nsHTMLFramesetBorderFrame* aBorder,
  1278                                     WidgetGUIEvent*            aEvent)
  1280 #if 0
  1281   int32_t index;
  1282   IndexOf(aBorder, index);
  1283   NS_ASSERTION((nullptr != aBorder) && (index >= 0), "invalid dragger");
  1284 #endif
  1286   nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
  1288   mDragger = aBorder;
  1290   mFirstDragPoint = LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint);
  1292   // Store the original frame sizes
  1293   if (mDragger->mVertical) {
  1294     mPrevNeighborOrigSize = mColSizes[mDragger->mPrevNeighbor];
  1295     mNextNeighborOrigSize = mColSizes[mDragger->mNextNeighbor];
  1296   } else {
  1297     mPrevNeighborOrigSize = mRowSizes[mDragger->mPrevNeighbor];
  1298     mNextNeighborOrigSize = mRowSizes[mDragger->mNextNeighbor];
  1301   gDragInProgress = true;
  1305 void
  1306 nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
  1307                                WidgetGUIEvent* aEvent)
  1309   // if the capture ended, reset the drag state
  1310   if (nsIPresShell::GetCapturingContent() != GetContent()) {
  1311     mDragger = nullptr;
  1312     gDragInProgress = false;
  1313     return;
  1316   int32_t change; // measured positive from left-to-right or top-to-bottom
  1317   nsWeakFrame weakFrame(this);
  1318   if (mDragger->mVertical) {
  1319     change = aPresContext->DevPixelsToAppUnits(aEvent->refPoint.x - mFirstDragPoint.x);
  1320     if (change > mNextNeighborOrigSize - mMinDrag) {
  1321       change = mNextNeighborOrigSize - mMinDrag;
  1322     } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
  1323       change = mMinDrag - mPrevNeighborOrigSize;
  1325     mColSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
  1326     mColSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
  1328     if (change != 0) {
  1329       // Recompute the specs from the new sizes.
  1330       nscoord width = mRect.width - (mNumCols - 1) * GetBorderWidth(aPresContext, true);
  1331       HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
  1332       NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
  1333       const nsFramesetSpec* colSpecs = nullptr;
  1334       ourContent->GetColSpec(&mNumCols, &colSpecs);
  1335       nsAutoString newColAttr;
  1336       GenerateRowCol(aPresContext, width, mNumCols, colSpecs, mColSizes,
  1337                      newColAttr);
  1338       // Setting the attr will trigger a reflow
  1339       mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::cols, newColAttr, true);
  1341   } else {
  1342     change = aPresContext->DevPixelsToAppUnits(aEvent->refPoint.y - mFirstDragPoint.y);
  1343     if (change > mNextNeighborOrigSize - mMinDrag) {
  1344       change = mNextNeighborOrigSize - mMinDrag;
  1345     } else if (change <= mMinDrag - mPrevNeighborOrigSize) {
  1346       change = mMinDrag - mPrevNeighborOrigSize;
  1348     mRowSizes[mDragger->mPrevNeighbor] = mPrevNeighborOrigSize + change;
  1349     mRowSizes[mDragger->mNextNeighbor] = mNextNeighborOrigSize - change;
  1351     if (change != 0) {
  1352       // Recompute the specs from the new sizes.
  1353       nscoord height = mRect.height - (mNumRows - 1) * GetBorderWidth(aPresContext, true);
  1354       HTMLFrameSetElement* ourContent = HTMLFrameSetElement::FromContent(mContent);
  1355       NS_ASSERTION(ourContent, "Someone gave us a broken frameset element!");
  1356       const nsFramesetSpec* rowSpecs = nullptr;
  1357       ourContent->GetRowSpec(&mNumRows, &rowSpecs);
  1358       nsAutoString newRowAttr;
  1359       GenerateRowCol(aPresContext, height, mNumRows, rowSpecs, mRowSizes,
  1360                      newRowAttr);
  1361       // Setting the attr will trigger a reflow
  1362       mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::rows, newRowAttr, true);
  1366   ENSURE_TRUE(weakFrame.IsAlive());
  1367   if (change != 0) {
  1368     mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this);
  1372 void
  1373 nsHTMLFramesetFrame::EndMouseDrag(nsPresContext* aPresContext)
  1375   nsIPresShell::SetCapturingContent(nullptr, 0);
  1376   mDragger = nullptr;
  1377   gDragInProgress = false;
  1380 nsIFrame*
  1381 NS_NewHTMLFramesetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1383 #ifdef DEBUG
  1384   const nsStyleDisplay* disp = aContext->StyleDisplay();
  1385   NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(),
  1386                "Framesets should not be positioned and should not float");
  1387 #endif
  1389   return new (aPresShell) nsHTMLFramesetFrame(aContext);
  1392 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetFrame)
  1394 /*******************************************************************************
  1395  * nsHTMLFramesetBorderFrame
  1396  ******************************************************************************/
  1397 nsHTMLFramesetBorderFrame::nsHTMLFramesetBorderFrame(nsStyleContext* aContext,
  1398                                                      int32_t aWidth,
  1399                                                      bool    aVertical,
  1400                                                      bool    aVisibility)
  1401   : nsLeafFrame(aContext), mWidth(aWidth), mVertical(aVertical), mVisibility(aVisibility)
  1403    mVisibilityOverride = false;
  1404    mCanResize    = true;
  1405    mColor        = NO_COLOR;
  1406    mPrevNeighbor = 0;
  1407    mNextNeighbor = 0;
  1410 nsHTMLFramesetBorderFrame::~nsHTMLFramesetBorderFrame()
  1412   //printf("nsHTMLFramesetBorderFrame destructor %p \n", this);
  1415 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBorderFrame)
  1417 nscoord nsHTMLFramesetBorderFrame::GetIntrinsicWidth()
  1419   // No intrinsic width
  1420   return 0;
  1423 nscoord nsHTMLFramesetBorderFrame::GetIntrinsicHeight()
  1425   // No intrinsic height
  1426   return 0;
  1429 void nsHTMLFramesetBorderFrame::SetVisibility(bool aVisibility)
  1431   mVisibility = aVisibility;
  1434 void nsHTMLFramesetBorderFrame::SetColor(nscolor aColor)
  1436   mColor = aColor;
  1440 nsresult
  1441 nsHTMLFramesetBorderFrame::Reflow(nsPresContext*           aPresContext,
  1442                                   nsHTMLReflowMetrics&     aDesiredSize,
  1443                                   const nsHTMLReflowState& aReflowState,
  1444                                   nsReflowStatus&          aStatus)
  1446   DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBorderFrame");
  1447   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
  1449   // Override Reflow(), since we don't want to deal with what our
  1450   // computed values are.
  1451   SizeToAvailSize(aReflowState, aDesiredSize);
  1453   aDesiredSize.SetOverflowAreasToDesiredBounds();
  1454   aStatus = NS_FRAME_COMPLETE;
  1455   return NS_OK;
  1458 class nsDisplayFramesetBorder : public nsDisplayItem {
  1459 public:
  1460   nsDisplayFramesetBorder(nsDisplayListBuilder* aBuilder,
  1461                           nsHTMLFramesetBorderFrame* aFrame)
  1462     : nsDisplayItem(aBuilder, aFrame) {
  1463     MOZ_COUNT_CTOR(nsDisplayFramesetBorder);
  1465 #ifdef NS_BUILD_REFCNT_LOGGING
  1466   virtual ~nsDisplayFramesetBorder() {
  1467     MOZ_COUNT_DTOR(nsDisplayFramesetBorder);
  1469 #endif
  1471   // REVIEW: see old GetFrameForPoint
  1472   // Receives events in its bounds
  1473   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  1474                        HitTestState* aState,
  1475                        nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE {
  1476     aOutFrames->AppendElement(mFrame);
  1478   virtual void Paint(nsDisplayListBuilder* aBuilder,
  1479                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
  1480   NS_DISPLAY_DECL_NAME("FramesetBorder", TYPE_FRAMESET_BORDER)
  1481 };
  1483 void nsDisplayFramesetBorder::Paint(nsDisplayListBuilder* aBuilder,
  1484                                     nsRenderingContext* aCtx)
  1486   static_cast<nsHTMLFramesetBorderFrame*>(mFrame)->
  1487     PaintBorder(*aCtx, ToReferenceFrame());
  1490 void
  1491 nsHTMLFramesetBorderFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1492                                             const nsRect&           aDirtyRect,
  1493                                             const nsDisplayListSet& aLists)
  1495   aLists.Content()->AppendNewToTop(
  1496     new (aBuilder) nsDisplayFramesetBorder(aBuilder, this));
  1499 void nsHTMLFramesetBorderFrame::PaintBorder(nsRenderingContext& aRenderingContext,
  1500                                             nsPoint aPt)
  1502   nscolor WHITE    = NS_RGB(255, 255, 255);
  1504   nscolor bgColor =
  1505     LookAndFeel::GetColor(LookAndFeel::eColorID_WidgetBackground,
  1506                           NS_RGB(200,200,200));
  1507   nscolor fgColor =
  1508     LookAndFeel::GetColor(LookAndFeel::eColorID_WidgetForeground,
  1509                           NS_RGB(0,0,0));
  1510   nscolor hltColor =
  1511     LookAndFeel::GetColor(LookAndFeel::eColorID_Widget3DHighlight,
  1512                           NS_RGB(255,255,255));
  1513   nscolor sdwColor =
  1514     LookAndFeel::GetColor(LookAndFeel::eColorID_Widget3DShadow,
  1515                           NS_RGB(128,128,128));
  1517   nsRenderingContext::AutoPushTranslation
  1518     translate(&aRenderingContext, aPt);
  1520   nscoord widthInPixels = nsPresContext::AppUnitsToIntCSSPixels(mWidth);
  1521   nscoord pixelWidth    = nsPresContext::CSSPixelsToAppUnits(1);
  1523   if (widthInPixels <= 0)
  1524     return;
  1526   nsPoint start(0,0);
  1527   nsPoint end((mVertical) ? 0 : mRect.width, (mVertical) ? mRect.height : 0);
  1529   nscolor color = WHITE;
  1530   if (mVisibility || mVisibilityOverride) {
  1531     color = (NO_COLOR == mColor) ? bgColor : mColor;
  1533   aRenderingContext.SetColor(color);
  1534   // draw grey or white first
  1535   for (int i = 0; i < widthInPixels; i++) {
  1536     aRenderingContext.DrawLine (start, end);
  1537     if (mVertical) {
  1538       start.x += pixelWidth;
  1539       end.x =  start.x;
  1540     } else {
  1541       start.y += pixelWidth;
  1542       end.y =  start.y;
  1546   if (!mVisibility && !mVisibilityOverride)
  1547     return;
  1549   if (widthInPixels >= 5) {
  1550     aRenderingContext.SetColor(hltColor);
  1551     start.x = (mVertical) ? pixelWidth : 0;
  1552     start.y = (mVertical) ? 0 : pixelWidth;
  1553     end.x   = (mVertical) ? start.x : mRect.width;
  1554     end.y   = (mVertical) ? mRect.height : start.y;
  1555     aRenderingContext.DrawLine(start, end);
  1558   if (widthInPixels >= 2) {
  1559     aRenderingContext.SetColor(sdwColor);
  1560     start.x = (mVertical) ? mRect.width - (2 * pixelWidth) : 0;
  1561     start.y = (mVertical) ? 0 : mRect.height - (2 * pixelWidth);
  1562     end.x   = (mVertical) ? start.x : mRect.width;
  1563     end.y   = (mVertical) ? mRect.height : start.y;
  1564     aRenderingContext.DrawLine(start, end);
  1567   if (widthInPixels >= 1) {
  1568     aRenderingContext.SetColor(fgColor);
  1569     start.x = (mVertical) ? mRect.width - pixelWidth : 0;
  1570     start.y = (mVertical) ? 0 : mRect.height - pixelWidth;
  1571     end.x   = (mVertical) ? start.x : mRect.width;
  1572     end.y   = (mVertical) ? mRect.height : start.y;
  1573     aRenderingContext.DrawLine(start, end);
  1578 nsresult
  1579 nsHTMLFramesetBorderFrame::HandleEvent(nsPresContext* aPresContext,
  1580                                        WidgetGUIEvent* aEvent,
  1581                                        nsEventStatus* aEventStatus)
  1583   NS_ENSURE_ARG_POINTER(aEventStatus);
  1584   *aEventStatus = nsEventStatus_eIgnore;
  1586   //XXX Mouse setting logic removed.  The remaining logic should also move.
  1587   if (!mCanResize) {
  1588     return NS_OK;
  1591   if (aEvent->message == NS_MOUSE_BUTTON_DOWN &&
  1592       aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
  1593     nsHTMLFramesetFrame* parentFrame = do_QueryFrame(GetParent());
  1594     if (parentFrame) {
  1595       parentFrame->StartMouseDrag(aPresContext, this, aEvent);
  1596       *aEventStatus = nsEventStatus_eConsumeNoDefault;
  1599   return NS_OK;
  1602 nsresult
  1603 nsHTMLFramesetBorderFrame::GetCursor(const nsPoint&    aPoint,
  1604                                      nsIFrame::Cursor& aCursor)
  1606   if (!mCanResize) {
  1607     aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
  1608   } else {
  1609     aCursor.mCursor = (mVertical) ? NS_STYLE_CURSOR_EW_RESIZE : NS_STYLE_CURSOR_NS_RESIZE;
  1611   return NS_OK;
  1614 #ifdef DEBUG_FRAME_DUMP
  1615 nsresult nsHTMLFramesetBorderFrame::GetFrameName(nsAString& aResult) const
  1617   return MakeFrameName(NS_LITERAL_STRING("FramesetBorder"), aResult);
  1619 #endif
  1621 /*******************************************************************************
  1622  * nsHTMLFramesetBlankFrame
  1623  ******************************************************************************/
  1625 NS_QUERYFRAME_HEAD(nsHTMLFramesetBlankFrame)
  1626   NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame)
  1627 NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame)
  1629 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame)
  1631 nsHTMLFramesetBlankFrame::~nsHTMLFramesetBlankFrame()
  1633   //printf("nsHTMLFramesetBlankFrame destructor %p \n", this);
  1636 nscoord nsHTMLFramesetBlankFrame::GetIntrinsicWidth()
  1638   // No intrinsic width
  1639   return 0;
  1642 nscoord nsHTMLFramesetBlankFrame::GetIntrinsicHeight()
  1644   // No intrinsic height
  1645   return 0;
  1648 nsresult
  1649 nsHTMLFramesetBlankFrame::Reflow(nsPresContext*           aPresContext,
  1650                                  nsHTMLReflowMetrics&     aDesiredSize,
  1651                                  const nsHTMLReflowState& aReflowState,
  1652                                  nsReflowStatus&          aStatus)
  1654   DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetBlankFrame");
  1656   // Override Reflow(), since we don't want to deal with what our
  1657   // computed values are.
  1658   SizeToAvailSize(aReflowState, aDesiredSize);
  1660   aDesiredSize.SetOverflowAreasToDesiredBounds();
  1661   aStatus = NS_FRAME_COMPLETE;
  1662   return NS_OK;
  1665 class nsDisplayFramesetBlank : public nsDisplayItem {
  1666 public:
  1667   nsDisplayFramesetBlank(nsDisplayListBuilder* aBuilder,
  1668                          nsIFrame* aFrame) :
  1669     nsDisplayItem(aBuilder, aFrame) {
  1670     MOZ_COUNT_CTOR(nsDisplayFramesetBlank);
  1672 #ifdef NS_BUILD_REFCNT_LOGGING
  1673   virtual ~nsDisplayFramesetBlank() {
  1674     MOZ_COUNT_DTOR(nsDisplayFramesetBlank);
  1676 #endif
  1678   virtual void Paint(nsDisplayListBuilder* aBuilder,
  1679                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
  1680   NS_DISPLAY_DECL_NAME("FramesetBlank", TYPE_FRAMESET_BLANK)
  1681 };
  1683 void nsDisplayFramesetBlank::Paint(nsDisplayListBuilder* aBuilder,
  1684                                    nsRenderingContext* aCtx)
  1686   nscolor white = NS_RGB(255,255,255);
  1687   aCtx->SetColor(white);
  1688   aCtx->FillRect(mVisibleRect);
  1691 void
  1692 nsHTMLFramesetBlankFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1693                                            const nsRect&           aDirtyRect,
  1694                                            const nsDisplayListSet& aLists)
  1696   aLists.Content()->AppendNewToTop(
  1697     new (aBuilder) nsDisplayFramesetBlank(aBuilder, this));

mercurial