Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLFrameSetElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsIDOMHTMLFrameSetElement.h"
00038 #include "nsIDOMEventReceiver.h"
00039 #include "nsGenericHTMLElement.h"
00040 #include "nsHTMLAtoms.h"
00041 #include "nsStyleConsts.h"
00042 #include "nsPresContext.h"
00043 #include "nsIFrameSetElement.h"
00044 #include "nsIHTMLDocument.h"
00045 #include "nsIDocument.h"
00046 
00047 class nsHTMLFrameSetElement : public nsGenericHTMLElement,
00048                               public nsIDOMHTMLFrameSetElement,
00049                               public nsIFrameSetElement
00050 {
00051 public:
00052   nsHTMLFrameSetElement(nsINodeInfo *aNodeInfo);
00053   virtual ~nsHTMLFrameSetElement();
00054 
00055   // nsISupports
00056   NS_DECL_ISUPPORTS_INHERITED
00057 
00058   // nsIDOMNode
00059   NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLElement::)
00060 
00061   // nsIDOMElement
00062   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
00063 
00064   // nsIDOMHTMLElement
00065   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
00066 
00067   // nsIDOMHTMLFrameSetElement
00068   NS_DECL_NSIDOMHTMLFRAMESETELEMENT
00069 
00070   // These override the SetAttr methods in nsGenericHTMLElement (need
00071   // both here to silence compiler warnings).
00072   nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00073                    const nsAString& aValue, PRBool aNotify)
00074   {
00075     return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
00076   }
00077   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00078                            nsIAtom* aPrefix, const nsAString& aValue,
00079                            PRBool aNotify);
00080 
00081   // nsIFramesetElement
00082   NS_IMETHOD GetRowSpec(PRInt32 *aNumValues, const nsFramesetSpec** aSpecs);
00083   NS_IMETHOD GetColSpec(PRInt32 *aNumValues, const nsFramesetSpec** aSpecs);
00084 
00085   virtual PRBool ParseAttribute(nsIAtom* aAttribute,
00086                                 const nsAString& aValue,
00087                                 nsAttrValue& aResult);
00088   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
00089                                               PRInt32 aModType) const;
00090 private:
00091   nsresult ParseRowCol(const nsAString& aValue,
00092                        PRInt32&         aNumSpecs,
00093                        nsFramesetSpec** aSpecs);
00094 
00098   PRInt32          mNumRows;
00102   PRInt32          mNumCols;
00107   nsChangeHint      mCurrentRowColHint;
00111   nsAutoArrayPtr<nsFramesetSpec>  mRowSpecs; // parsed, non-computed dimensions
00115   nsAutoArrayPtr<nsFramesetSpec>  mColSpecs; // parsed, non-computed dimensions
00116 };
00117 
00118 NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet)
00119 
00120 
00121 nsHTMLFrameSetElement::nsHTMLFrameSetElement(nsINodeInfo *aNodeInfo)
00122   : nsGenericHTMLElement(aNodeInfo), mNumRows(0), mNumCols(0),
00123     mCurrentRowColHint(NS_STYLE_HINT_REFLOW)
00124 {
00125 }
00126 
00127 nsHTMLFrameSetElement::~nsHTMLFrameSetElement()
00128 {
00129 }
00130 
00131 
00132 NS_IMPL_ADDREF_INHERITED(nsHTMLFrameSetElement, nsGenericElement) 
00133 NS_IMPL_RELEASE_INHERITED(nsHTMLFrameSetElement, nsGenericElement) 
00134 
00135 
00136 // QueryInterface implementation for nsHTMLFrameSetElement
00137 NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLFrameSetElement,
00138                                     nsGenericHTMLElement)
00139   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLFrameSetElement)
00140   NS_INTERFACE_MAP_ENTRY(nsIFrameSetElement)
00141   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLFrameSetElement)
00142 NS_HTML_CONTENT_INTERFACE_MAP_END
00143 
00144 
00145 NS_IMPL_DOM_CLONENODE(nsHTMLFrameSetElement)
00146 
00147 
00148 NS_IMPL_STRING_ATTR(nsHTMLFrameSetElement, Cols, cols)
00149 NS_IMPL_STRING_ATTR(nsHTMLFrameSetElement, Rows, rows)
00150 
00151 nsresult
00152 nsHTMLFrameSetElement::SetAttr(PRInt32 aNameSpaceID,
00153                                nsIAtom* aAttribute,
00154                                nsIAtom* aPrefix,
00155                                const nsAString& aValue,
00156                                PRBool aNotify)
00157 {
00158   nsresult rv;
00159   /* The main goal here is to see whether the _number_ of rows or
00160    *  columns has changed.  If it has, we need to reframe; otherwise
00161    *  we want to reflow.  So we set mCurrentRowColHint here, then call
00162    *  nsGenericHTMLElement::SetAttr, which will end up calling
00163    *  GetAttributeChangeHint and notifying layout with that hint.
00164    *  Once nsGenericHTMLElement::SetAttr returns, we want to go back to our
00165    *  normal hint, which is NS_STYLE_HINT_REFLOW.
00166    */
00167   if (aAttribute == nsHTMLAtoms::rows && aNameSpaceID == kNameSpaceID_None) {
00168     PRInt32 oldRows = mNumRows;
00169     ParseRowCol(aValue, mNumRows, getter_Transfers(mRowSpecs));
00170     
00171     if (mNumRows != oldRows) {
00172       mCurrentRowColHint = NS_STYLE_HINT_FRAMECHANGE;
00173     }
00174   } else if (aAttribute == nsHTMLAtoms::cols &&
00175              aNameSpaceID == kNameSpaceID_None) {
00176     PRInt32 oldCols = mNumCols;
00177     ParseRowCol(aValue, mNumCols, getter_Transfers(mColSpecs));
00178 
00179     if (mNumCols != oldCols) {
00180       mCurrentRowColHint = NS_STYLE_HINT_FRAMECHANGE;
00181     }
00182   }
00183   
00184   rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute, aPrefix,
00185                                      aValue, aNotify);
00186   mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
00187   
00188   return rv;
00189 }
00190 
00191 NS_IMETHODIMP
00192 nsHTMLFrameSetElement::GetRowSpec(PRInt32 *aNumValues,
00193                                   const nsFramesetSpec** aSpecs)
00194 {
00195   NS_PRECONDITION(aNumValues, "Must have a pointer to an integer here!");
00196   NS_PRECONDITION(aSpecs, "Must have a pointer to an array of nsFramesetSpecs");
00197   *aNumValues = 0;
00198   *aSpecs = nsnull;
00199   
00200   if (!mRowSpecs) {
00201     const nsAttrValue* value = GetParsedAttr(nsHTMLAtoms::rows);
00202     if (value && value->Type() == nsAttrValue::eString) {
00203       nsresult rv = ParseRowCol(value->GetStringValue(), mNumRows,
00204                                 getter_Transfers(mRowSpecs));
00205       NS_ENSURE_SUCCESS(rv, rv);
00206     }
00207 
00208     if (!mRowSpecs) {  // we may not have had an attr or had an empty attr
00209       mRowSpecs = new nsFramesetSpec[1];
00210       if (!mRowSpecs) {
00211         mNumRows = 0;
00212         return NS_ERROR_OUT_OF_MEMORY;
00213       }
00214       mNumRows = 1;
00215       mRowSpecs[0].mUnit  = eFramesetUnit_Relative;
00216       mRowSpecs[0].mValue = 1;
00217     }
00218   }
00219 
00220   *aSpecs = mRowSpecs;
00221   *aNumValues = mNumRows;
00222   return NS_OK;
00223 }
00224 
00225 NS_IMETHODIMP
00226 nsHTMLFrameSetElement::GetColSpec(PRInt32 *aNumValues,
00227                                   const nsFramesetSpec** aSpecs)
00228 {
00229   NS_PRECONDITION(aNumValues, "Must have a pointer to an integer here!");
00230   NS_PRECONDITION(aSpecs, "Must have a pointer to an array of nsFramesetSpecs");
00231   *aNumValues = 0;
00232   *aSpecs = nsnull;
00233 
00234   if (!mColSpecs) {
00235     const nsAttrValue* value = GetParsedAttr(nsHTMLAtoms::cols);
00236     if (value && value->Type() == nsAttrValue::eString) {
00237       nsresult rv = ParseRowCol(value->GetStringValue(), mNumCols,
00238                                 getter_Transfers(mColSpecs));
00239       NS_ENSURE_SUCCESS(rv, rv);
00240     }
00241 
00242     if (!mColSpecs) {  // we may not have had an attr or had an empty attr
00243       mColSpecs = new nsFramesetSpec[1];
00244       if (!mColSpecs) {
00245         mNumCols = 0;
00246         return NS_ERROR_OUT_OF_MEMORY;
00247       }
00248       mNumCols = 1;
00249       mColSpecs[0].mUnit  = eFramesetUnit_Relative;
00250       mColSpecs[0].mValue = 1;
00251     }
00252   }
00253 
00254   *aSpecs = mColSpecs;
00255   *aNumValues = mNumCols;
00256   return NS_OK;
00257 }
00258 
00259 
00260 PRBool
00261 nsHTMLFrameSetElement::ParseAttribute(nsIAtom* aAttribute,
00262                                       const nsAString& aValue,
00263                                       nsAttrValue& aResult)
00264 {
00265   if (aAttribute == nsHTMLAtoms::bordercolor) {
00266     return aResult.ParseColor(aValue, GetOwnerDoc());
00267   } 
00268   if (aAttribute == nsHTMLAtoms::frameborder) {
00269     return nsGenericHTMLElement::ParseFrameborderValue(aValue, aResult);
00270   } 
00271   if (aAttribute == nsHTMLAtoms::border) {
00272     return aResult.ParseIntWithBounds(aValue, 0, 100);
00273   }
00274 
00275   return nsGenericHTMLElement::ParseAttribute(aAttribute, aValue, aResult);
00276 }
00277 
00278 nsChangeHint
00279 nsHTMLFrameSetElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
00280                                               PRInt32 aModType) const
00281 {
00282   nsChangeHint retval =
00283     nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
00284   if (aAttribute == nsHTMLAtoms::rows ||
00285       aAttribute == nsHTMLAtoms::cols) {
00286     NS_UpdateHint(retval, mCurrentRowColHint);
00287   }
00288   return retval;
00289 }
00290 
00294 nsresult
00295 nsHTMLFrameSetElement::ParseRowCol(const nsAString & aValue,
00296                                    PRInt32& aNumSpecs,
00297                                    nsFramesetSpec** aSpecs) 
00298 {
00299   if (aValue.IsEmpty()) {
00300     aNumSpecs = 0;
00301     *aSpecs = nsnull;
00302     return NS_OK;
00303   }
00304 
00305   static const PRUnichar sAster('*');
00306   static const PRUnichar sPercent('%');
00307   static const PRUnichar sComma(',');
00308 
00309   nsAutoString spec(aValue);
00310   // remove whitespace (Bug 33699) and quotation marks (bug 224598)
00311   // also remove leading/trailing commas (bug 31482)
00312   spec.StripChars(" \n\r\t\"\'");
00313   spec.Trim(",");
00314   
00315   // Count the commas 
00316   PRInt32 commaX = spec.FindChar(sComma);
00317   PRInt32 count = 1;
00318   while (commaX != kNotFound) {
00319     count++;
00320     commaX = spec.FindChar(sComma, commaX + 1);
00321   }
00322 
00323   nsFramesetSpec* specs = new nsFramesetSpec[count];
00324   if (!specs) {
00325     *aSpecs = nsnull;
00326     aNumSpecs = 0;
00327     return NS_ERROR_OUT_OF_MEMORY;
00328   }
00329 
00330   // Pre-grab the compat mode; we may need it later in the loop.
00331   nsCompatibility mode = eCompatibility_FullStandards;
00332   nsCOMPtr<nsIHTMLDocument> htmlDocument =
00333     do_QueryInterface(GetOwnerDoc());
00334   if (htmlDocument) {
00335     mode = htmlDocument->GetCompatibilityMode();
00336   }
00337       
00338   // Parse each comma separated token
00339 
00340   PRInt32 start = 0;
00341   PRInt32 specLen = spec.Length();
00342 
00343   for (PRInt32 i = 0; i < count; i++) {
00344     // Find our comma
00345     commaX = spec.FindChar(sComma, start);
00346     NS_ASSERTION(i == count - 1 || commaX != kNotFound,
00347                  "Failed to find comma, somehow");
00348     PRInt32 end = (commaX == kNotFound) ? specLen : commaX;
00349 
00350     // Note: If end == start then it means that the token has no
00351     // data in it other than a terminating comma (or the end of the spec).
00352     // So default to a fixed width of 0.
00353     specs[i].mUnit = eFramesetUnit_Fixed;
00354     specs[i].mValue = 0;
00355     if (end > start) {
00356       PRInt32 numberEnd = end;
00357       PRUnichar ch = spec.CharAt(numberEnd - 1);
00358       if (sAster == ch) {
00359         specs[i].mUnit = eFramesetUnit_Relative;
00360         numberEnd--;
00361       } else if (sPercent == ch) {
00362         specs[i].mUnit = eFramesetUnit_Percent;
00363         numberEnd--;
00364         // check for "*%"
00365         if (numberEnd > start) {
00366           ch = spec.CharAt(numberEnd - 1);
00367           if (sAster == ch) {
00368             specs[i].mUnit = eFramesetUnit_Relative;
00369             numberEnd--;
00370           }
00371         }
00372       }
00373 
00374       // Translate value to an integer
00375       nsAutoString token;
00376       spec.Mid(token, start, numberEnd - start);
00377 
00378       // Treat * as 1*
00379       if ((eFramesetUnit_Relative == specs[i].mUnit) &&
00380         (0 == token.Length())) {
00381         specs[i].mValue = 1;
00382       }
00383       else {
00384         // Otherwise just convert to integer.
00385         PRInt32 err;
00386         specs[i].mValue = token.ToInteger(&err);
00387         if (err) {
00388           specs[i].mValue = 0;
00389         }
00390       }
00391 
00392       // Treat 0* as 1* in quirks mode (bug 40383)
00393       if (eCompatibility_NavQuirks == mode) {
00394         if ((eFramesetUnit_Relative == specs[i].mUnit) &&
00395           (0 == specs[i].mValue)) {
00396           specs[i].mValue = 1;
00397         }
00398       }
00399         
00400       // Catch zero and negative frame sizes for Nav compatability
00401       // Nav resized absolute and relative frames to "1" and
00402       // percent frames to an even percentage of the width
00403       //
00404       //if ((eCompatibility_NavQuirks == aMode) && (specs[i].mValue <= 0)) {
00405       //  if (eFramesetUnit_Percent == specs[i].mUnit) {
00406       //    specs[i].mValue = 100 / count;
00407       //  } else {
00408       //    specs[i].mValue = 1;
00409       //  }
00410       //} else {
00411 
00412       // In standards mode, just set negative sizes to zero
00413       if (specs[i].mValue < 0) {
00414         specs[i].mValue = 0;
00415       }
00416       start = end + 1;
00417     }
00418   }
00419 
00420   aNumSpecs = count;
00421   // Transfer ownership to caller here
00422   *aSpecs = specs;
00423   
00424   return NS_OK;
00425 }
00426