Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLTableElement.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 "nsIDOMHTMLTableElement.h"
00038 #include "nsIDOMHTMLTableCaptionElem.h"
00039 #include "nsIDOMHTMLTableSectionElem.h"
00040 #include "nsCOMPtr.h"
00041 #include "nsIDOMEventReceiver.h"
00042 #include "nsDOMError.h"
00043 #include "nsContentList.h"
00044 #include "nsGenericDOMHTMLCollection.h"
00045 #include "nsMappedAttributes.h"
00046 #include "nsGenericHTMLElement.h"
00047 #include "nsHTMLAtoms.h"
00048 #include "nsStyleConsts.h"
00049 #include "nsPresContext.h"
00050 #include "nsHTMLParts.h"
00051 #include "nsRuleData.h"
00052 #include "nsStyleContext.h"
00053 #include "nsIDocument.h"
00054 
00055 /* for collections */
00056 #include "nsIDOMElement.h"
00057 #include "nsGenericHTMLElement.h"
00058 /* end for collections */
00059 
00060 class TableRowsCollection;
00061 
00062 class nsHTMLTableElement :  public nsGenericHTMLElement,
00063                             public nsIDOMHTMLTableElement
00064 {
00065 public:
00066   nsHTMLTableElement(nsINodeInfo *aNodeInfo);
00067   virtual ~nsHTMLTableElement();
00068 
00069   // nsISupports
00070   NS_DECL_ISUPPORTS_INHERITED
00071 
00072   // nsIDOMNode
00073   NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLElement::)
00074 
00075   // nsIDOMElement
00076   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
00077 
00078   // nsIDOMHTMLElement
00079   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
00080 
00081   // nsIDOMHTMLTableElement
00082   NS_DECL_NSIDOMHTMLTABLEELEMENT
00083 
00084   virtual PRBool ParseAttribute(nsIAtom* aAttribute,
00085                                 const nsAString& aValue,
00086                                 nsAttrValue& aResult);
00087   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
00088   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
00089 
00090 protected:
00091   already_AddRefed<nsIDOMHTMLTableSectionElement> GetSection(nsIAtom *aTag);
00092 
00093   nsRefPtr<nsContentList> mTBodies;
00094   TableRowsCollection *mRows;
00095 };
00096 
00097 
00098 /* ------------------------------ TableRowsCollection -------------------------------- */
00103 class TableRowsCollection : public nsGenericDOMHTMLCollection 
00104 {
00105 public:
00106   TableRowsCollection(nsHTMLTableElement *aParent);
00107   virtual ~TableRowsCollection();
00108 
00109   NS_IMETHOD    GetLength(PRUint32* aLength);
00110   NS_IMETHOD    Item(PRUint32 aIndex, nsIDOMNode** aReturn);
00111   NS_IMETHOD    NamedItem(const nsAString& aName,
00112                           nsIDOMNode** aReturn);
00113 
00114   NS_IMETHOD    ParentDestroyed();
00115 
00116 protected:
00117   nsHTMLTableElement * mParent;
00118 };
00119 
00120 
00121 TableRowsCollection::TableRowsCollection(nsHTMLTableElement *aParent)
00122   : nsGenericDOMHTMLCollection()
00123 {
00124   mParent = aParent;
00125 }
00126 
00127 TableRowsCollection::~TableRowsCollection()
00128 {
00129   // we do NOT have a ref-counted reference to mParent, so do NOT
00130   // release it!  this is to avoid circular references.  The
00131   // instantiator who provided mParent is responsible for managing our
00132   // reference for us.
00133 }
00134 
00135 // Macro that can be used to avoid copy/pasting code to iterate over the
00136 // rowgroups.  _code should be the code to execute for each rowgroup.  The
00137 // rowgroups will be in the nsCOMPtr<nsIDOMHTMLTableSectionElement> named
00138 // rowGroup.  Note that this may be null at any time.  This macro assumes an
00139 // nsresult named |rv| is in scope.
00140 #define DO_FOR_EACH_ROWGROUP(_code)                                  \
00141   PR_BEGIN_MACRO                                                     \
00142     if (mParent) {                                                   \
00143       /* THead */                                                    \
00144       nsCOMPtr<nsIDOMHTMLTableSectionElement> rowGroup;              \
00145       rv = mParent->GetTHead(getter_AddRefs(rowGroup));              \
00146       NS_ENSURE_SUCCESS(rv, rv);                                     \
00147       do { /* gives scoping */                                       \
00148         _code                                                        \
00149       } while (0);                                                   \
00150       nsCOMPtr<nsIDOMHTMLCollection> _tbodies;                       \
00151       /* TBodies */                                                  \
00152       rv = mParent->GetTBodies(getter_AddRefs(_tbodies));            \
00153       NS_ENSURE_SUCCESS(rv, rv);                                     \
00154       if (_tbodies) {                                                \
00155         nsCOMPtr<nsIDOMNode> _node;                                  \
00156         PRUint32 _tbodyIndex = 0;                                    \
00157         rv = _tbodies->Item(_tbodyIndex, getter_AddRefs(_node));     \
00158         NS_ENSURE_SUCCESS(rv, rv);                                   \
00159         while (_node) {                                              \
00160           rowGroup = do_QueryInterface(_node);                       \
00161           do { /* gives scoping */                                   \
00162             _code                                                    \
00163           } while (0);                                               \
00164           rv = _tbodies->Item(++_tbodyIndex, getter_AddRefs(_node)); \
00165           NS_ENSURE_SUCCESS(rv, rv);                                 \
00166         }                                                            \
00167       }                                                              \
00168       /* TFoot */                                                    \
00169       rv = mParent->GetTFoot(getter_AddRefs(rowGroup));              \
00170       NS_ENSURE_SUCCESS(rv, rv);                                     \
00171       do { /* gives scoping */                                       \
00172         _code                                                        \
00173       } while (0);                                                   \
00174     }                                                                \
00175   PR_END_MACRO
00176 
00177 static PRUint32
00178 CountRowsInRowGroup(nsIDOMHTMLTableSectionElement* aRowGroup)
00179 {
00180   PRUint32 length = 0;
00181   
00182   if (aRowGroup) {
00183     nsCOMPtr<nsIDOMHTMLCollection> rows;
00184     aRowGroup->GetRows(getter_AddRefs(rows));
00185     
00186     if (rows) {
00187       rows->GetLength(&length);
00188     }
00189   }
00190   
00191   return length;
00192 }
00193 
00194 // we re-count every call.  A better implementation would be to set
00195 // ourselves up as an observer of contentAppended, contentInserted,
00196 // and contentDeleted
00197 NS_IMETHODIMP 
00198 TableRowsCollection::GetLength(PRUint32* aLength)
00199 {
00200   *aLength=0;
00201   nsresult rv = NS_OK;
00202 
00203   DO_FOR_EACH_ROWGROUP(
00204     *aLength += CountRowsInRowGroup(rowGroup);
00205   );
00206 
00207   return rv;
00208 }
00209 
00210 // Returns the number of items in the row group, only if *aItem ends
00211 // up null.  Otherwise, returns 0.
00212 static PRUint32
00213 GetItemOrCountInRowGroup(nsIDOMHTMLTableSectionElement* aRowGroup,
00214                          PRUint32 aIndex, nsIDOMNode** aItem)
00215 {
00216   NS_PRECONDITION(aItem, "Null out param");
00217 
00218   *aItem = nsnull;
00219   PRUint32 length = 0;
00220   
00221   if (aRowGroup) {
00222     nsCOMPtr<nsIDOMHTMLCollection> rows;
00223     aRowGroup->GetRows(getter_AddRefs(rows));
00224     
00225     if (rows) {
00226       rows->Item(aIndex, aItem);
00227       if (!*aItem) {
00228         rows->GetLength(&length);
00229       }
00230     }
00231   }
00232   
00233   return length;
00234 }
00235 
00236 // increments aReturn refcnt by 1
00237 NS_IMETHODIMP 
00238 TableRowsCollection::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
00239 {
00240   *aReturn = nsnull;
00241   nsresult rv = NS_OK;
00242 
00243   DO_FOR_EACH_ROWGROUP(
00244     PRUint32 count = GetItemOrCountInRowGroup(rowGroup, aIndex, aReturn);
00245     if (*aReturn) {
00246       return NS_OK; 
00247     }
00248 
00249     NS_ASSERTION(count <= aIndex, "GetItemOrCountInRowGroup screwed up");
00250     aIndex -= count;
00251   );
00252 
00253   return rv;
00254 }
00255 
00256 static nsresult
00257 GetNamedItemInRowGroup(nsIDOMHTMLTableSectionElement* aRowGroup,
00258                        const nsAString& aName, nsIDOMNode** aNamedItem)
00259 {
00260   *aNamedItem = nsnull;
00261   if (aRowGroup) {
00262     nsCOMPtr<nsIDOMHTMLCollection> rows;
00263     aRowGroup->GetRows(getter_AddRefs(rows));
00264     if (rows) {
00265       return rows->NamedItem(aName, aNamedItem);
00266     }
00267   }
00268 
00269   return NS_OK;
00270 }
00271 
00272 NS_IMETHODIMP 
00273 TableRowsCollection::NamedItem(const nsAString& aName,
00274                                nsIDOMNode** aReturn)
00275 {
00276   *aReturn = nsnull;
00277   nsresult rv = NS_OK;
00278   DO_FOR_EACH_ROWGROUP(
00279     rv = GetNamedItemInRowGroup(rowGroup, aName, aReturn);
00280     NS_ENSURE_SUCCESS(rv, rv);
00281     if (*aReturn) {
00282       return rv;
00283     }
00284   );
00285   return rv;
00286 }
00287 
00288 NS_IMETHODIMP
00289 TableRowsCollection::ParentDestroyed()
00290 {
00291   // see comment in destructor, do NOT release mParent!
00292   mParent = nsnull;
00293 
00294   return NS_OK;
00295 }
00296 
00297 /* -------------------------- nsHTMLTableElement --------------------------- */
00298 // the class declaration is at the top of this file
00299 
00300 
00301 NS_IMPL_NS_NEW_HTML_ELEMENT(Table)
00302 
00303 
00304 nsHTMLTableElement::nsHTMLTableElement(nsINodeInfo *aNodeInfo)
00305   : nsGenericHTMLElement(aNodeInfo)
00306 {
00307   mRows=nsnull;
00308 }
00309 
00310 nsHTMLTableElement::~nsHTMLTableElement()
00311 {
00312   if (mTBodies) {
00313     mTBodies->RootDestroyed();
00314   }
00315 
00316   if (mRows) {
00317     mRows->ParentDestroyed();
00318     NS_RELEASE(mRows);
00319   }
00320 }
00321 
00322 
00323 NS_IMPL_ADDREF_INHERITED(nsHTMLTableElement, nsGenericElement) 
00324 NS_IMPL_RELEASE_INHERITED(nsHTMLTableElement, nsGenericElement) 
00325 
00326 
00327 // QueryInterface implementation for nsHTMLTableElement
00328 NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLTableElement, nsGenericHTMLElement)
00329   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLTableElement)
00330   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLTableElement)
00331 NS_HTML_CONTENT_INTERFACE_MAP_END
00332 
00333 
00334 NS_IMPL_DOM_CLONENODE(nsHTMLTableElement)
00335 
00336 
00337 // the DOM spec says border, cellpadding, cellSpacing are all "wstring"
00338 // in fact, they are integers or they are meaningless.  so we store them
00339 // here as ints.
00340 
00341 NS_IMPL_STRING_ATTR(nsHTMLTableElement, Align, align)
00342 NS_IMPL_STRING_ATTR(nsHTMLTableElement, BgColor, bgcolor)
00343 NS_IMPL_STRING_ATTR(nsHTMLTableElement, Border, border)
00344 NS_IMPL_STRING_ATTR(nsHTMLTableElement, CellPadding, cellpadding)
00345 NS_IMPL_STRING_ATTR(nsHTMLTableElement, CellSpacing, cellspacing)
00346 NS_IMPL_STRING_ATTR(nsHTMLTableElement, Frame, frame)
00347 NS_IMPL_STRING_ATTR(nsHTMLTableElement, Rules, rules)
00348 NS_IMPL_STRING_ATTR(nsHTMLTableElement, Summary, summary)
00349 NS_IMPL_STRING_ATTR(nsHTMLTableElement, Width, width)
00350 
00351 
00352 NS_IMETHODIMP
00353 nsHTMLTableElement::GetCaption(nsIDOMHTMLTableCaptionElement** aValue)
00354 {
00355   *aValue = nsnull;
00356   nsCOMPtr<nsIDOMNode> child;
00357   GetFirstChild(getter_AddRefs(child));
00358 
00359   while (child) {
00360     nsCOMPtr<nsIDOMHTMLTableCaptionElement> caption(do_QueryInterface(child));
00361 
00362     if (caption) {
00363       *aValue = caption;
00364       NS_ADDREF(*aValue);
00365 
00366       break;
00367     }
00368 
00369     nsIDOMNode *temp = child.get();
00370     temp->GetNextSibling(getter_AddRefs(child));
00371   }
00372 
00373   return NS_OK;
00374 }
00375 
00376 NS_IMETHODIMP
00377 nsHTMLTableElement::SetCaption(nsIDOMHTMLTableCaptionElement* aValue)
00378 {
00379   nsresult rv = DeleteCaption();
00380 
00381   if (NS_SUCCEEDED(rv)) {
00382     if (aValue) {
00383       nsCOMPtr<nsIDOMNode> resultingChild;
00384       AppendChild(aValue, getter_AddRefs(resultingChild));
00385     }
00386   }
00387 
00388   return rv;
00389 }
00390 
00391 already_AddRefed<nsIDOMHTMLTableSectionElement>
00392 nsHTMLTableElement::GetSection(nsIAtom *aTag)
00393 {
00394   PRUint32 childCount = GetChildCount();
00395 
00396   nsCOMPtr<nsIDOMHTMLTableSectionElement> section;
00397 
00398   for (PRUint32 i = 0; i < childCount; ++i) {
00399     nsIContent *child = GetChildAt(i);
00400 
00401     section = do_QueryInterface(child);
00402 
00403     if (section && child->GetNodeInfo()->Equals(aTag)) {
00404       nsIDOMHTMLTableSectionElement *result = section;
00405       NS_ADDREF(result);
00406 
00407       return result;
00408     }
00409   }
00410 
00411   return nsnull;
00412 }
00413 
00414 NS_IMETHODIMP
00415 nsHTMLTableElement::GetTHead(nsIDOMHTMLTableSectionElement** aValue)
00416 {
00417   *aValue = GetSection(nsHTMLAtoms::thead).get();
00418 
00419   return NS_OK;
00420 }
00421 
00422 NS_IMETHODIMP
00423 nsHTMLTableElement::SetTHead(nsIDOMHTMLTableSectionElement* aValue)
00424 {
00425   nsresult rv = DeleteTHead();
00426   if (NS_FAILED(rv)) {
00427     return rv;
00428   }
00429 
00430   if (aValue) {
00431     nsCOMPtr<nsIDOMNode> child;
00432     rv = GetFirstChild(getter_AddRefs(child));
00433     if (NS_FAILED(rv)) {
00434       return rv;
00435     }
00436      
00437     nsCOMPtr<nsIDOMNode> resultChild;
00438     rv = InsertBefore(aValue, child, getter_AddRefs(resultChild));
00439   }
00440 
00441   return rv;
00442 }
00443 
00444 NS_IMETHODIMP
00445 nsHTMLTableElement::GetTFoot(nsIDOMHTMLTableSectionElement** aValue)
00446 {
00447   *aValue = GetSection(nsHTMLAtoms::tfoot).get();
00448 
00449   return NS_OK;
00450 }
00451 
00452 NS_IMETHODIMP
00453 nsHTMLTableElement::SetTFoot(nsIDOMHTMLTableSectionElement* aValue)
00454 {
00455   nsresult rv = DeleteTFoot();
00456   if (NS_SUCCEEDED(rv)) {
00457     if (aValue) {
00458       nsCOMPtr<nsIDOMNode> resultingChild;
00459       AppendChild(aValue, getter_AddRefs(resultingChild));
00460     }
00461   }
00462 
00463   return rv;
00464 }
00465 
00466 NS_IMETHODIMP
00467 nsHTMLTableElement::GetRows(nsIDOMHTMLCollection** aValue)
00468 {
00469   if (!mRows) {
00470     // XXX why was this here NS_ADDREF(nsHTMLAtoms::tr);
00471     mRows = new TableRowsCollection(this);
00472     NS_ENSURE_TRUE(mRows, NS_ERROR_OUT_OF_MEMORY);
00473 
00474     NS_ADDREF(mRows); // this table's reference, released in the destructor
00475   }
00476 
00477   *aValue = mRows;
00478   NS_ADDREF(*aValue);
00479 
00480   return NS_OK;
00481 }
00482 
00483 NS_IMETHODIMP
00484 nsHTMLTableElement::GetTBodies(nsIDOMHTMLCollection** aValue)
00485 {
00486   if (!mTBodies) {
00487     // Not using NS_GetContentList because this should not be cached
00488     mTBodies = new nsContentList(GetDocument(),
00489                                  nsHTMLAtoms::tbody,
00490                                  mNodeInfo->NamespaceID(),
00491                                  this,
00492                                  PR_FALSE);
00493 
00494     NS_ENSURE_TRUE(mTBodies, NS_ERROR_OUT_OF_MEMORY);
00495   }
00496 
00497   NS_ADDREF(*aValue = mTBodies);
00498   return NS_OK;
00499 }
00500 
00501 NS_IMETHODIMP
00502 nsHTMLTableElement::CreateTHead(nsIDOMHTMLElement** aValue)
00503 {
00504   *aValue = nsnull;
00505   nsresult rv = NS_OK;
00506   nsCOMPtr<nsIDOMHTMLTableSectionElement> head;
00507 
00508   GetTHead(getter_AddRefs(head));
00509 
00510   if (head) { // return the existing thead
00511     CallQueryInterface(head, aValue);
00512 
00513     NS_ASSERTION(*aValue, "head must be a DOMHTMLElement");
00514   }
00515   else
00516   { // create a new head rowgroup
00517     nsCOMPtr<nsINodeInfo> nodeInfo;
00518 
00519     nsContentUtils::NameChanged(mNodeInfo, nsHTMLAtoms::thead,
00520                                 getter_AddRefs(nodeInfo));
00521 
00522     nsCOMPtr<nsIContent> newHead = NS_NewHTMLTableSectionElement(nodeInfo);
00523 
00524     if (newHead) {
00525       nsCOMPtr<nsIDOMNode> child;
00526 
00527       rv = GetFirstChild(getter_AddRefs(child));
00528 
00529       if (NS_FAILED(rv)) {
00530         return rv;
00531       }
00532 
00533       CallQueryInterface(newHead, aValue);
00534 
00535       nsCOMPtr<nsIDOMNode> resultChild;
00536       rv = InsertBefore(*aValue, child, getter_AddRefs(resultChild));
00537     }
00538   }
00539 
00540   return NS_OK;
00541 }
00542 
00543 NS_IMETHODIMP
00544 nsHTMLTableElement::DeleteTHead()
00545 {
00546   nsCOMPtr<nsIDOMHTMLTableSectionElement> childToDelete;
00547   nsresult rv = GetTHead(getter_AddRefs(childToDelete));
00548 
00549   if ((NS_SUCCEEDED(rv)) && childToDelete) {
00550     nsCOMPtr<nsIDOMNode> resultingChild;
00551     // mInner does the notification
00552     RemoveChild(childToDelete, getter_AddRefs(resultingChild));
00553   }
00554 
00555   return NS_OK;
00556 }
00557 
00558 NS_IMETHODIMP
00559 nsHTMLTableElement::CreateTFoot(nsIDOMHTMLElement** aValue)
00560 {
00561   *aValue = nsnull;
00562   nsresult rv = NS_OK;
00563   nsCOMPtr<nsIDOMHTMLTableSectionElement> foot;
00564 
00565   GetTFoot(getter_AddRefs(foot));
00566 
00567   if (foot) { // return the existing tfoot
00568     CallQueryInterface(foot, aValue);
00569 
00570     NS_ASSERTION(*aValue, "foot must be a DOMHTMLElement");
00571   }
00572   else
00573   { // create a new foot rowgroup
00574     nsCOMPtr<nsINodeInfo> nodeInfo;
00575     nsContentUtils::NameChanged(mNodeInfo, nsHTMLAtoms::tfoot,
00576                                 getter_AddRefs(nodeInfo));
00577 
00578     nsCOMPtr<nsIContent> newFoot = NS_NewHTMLTableSectionElement(nodeInfo);
00579 
00580     if (newFoot) {
00581       rv = AppendChildTo(newFoot, PR_TRUE);
00582       CallQueryInterface(newFoot, aValue);
00583     }
00584   }
00585 
00586   return NS_OK;
00587 }
00588 
00589 NS_IMETHODIMP
00590 nsHTMLTableElement::DeleteTFoot()
00591 {
00592   nsCOMPtr<nsIDOMHTMLTableSectionElement> childToDelete;
00593   nsresult rv = GetTFoot(getter_AddRefs(childToDelete));
00594 
00595   if ((NS_SUCCEEDED(rv)) && childToDelete) {
00596     nsCOMPtr<nsIDOMNode> resultingChild;
00597     // mInner does the notification
00598     RemoveChild(childToDelete, getter_AddRefs(resultingChild));
00599   }
00600 
00601   return NS_OK;
00602 }
00603 
00604 NS_IMETHODIMP
00605 nsHTMLTableElement::CreateCaption(nsIDOMHTMLElement** aValue)
00606 {
00607   *aValue = nsnull;
00608   nsresult rv = NS_OK;
00609   nsCOMPtr<nsIDOMHTMLTableCaptionElement> caption;
00610 
00611   GetCaption(getter_AddRefs(caption));
00612 
00613   if (caption) { // return the existing thead
00614     CallQueryInterface(caption, aValue);
00615 
00616     NS_ASSERTION(*aValue, "caption must be a DOMHTMLElement");
00617   }
00618   else
00619   { // create a new head rowgroup
00620     nsCOMPtr<nsINodeInfo> nodeInfo;
00621     nsContentUtils::NameChanged(mNodeInfo, nsHTMLAtoms::caption,
00622                                 getter_AddRefs(nodeInfo));
00623 
00624     nsCOMPtr<nsIContent> newCaption = NS_NewHTMLTableCaptionElement(nodeInfo);
00625 
00626     if (newCaption) {
00627       rv = AppendChildTo(newCaption, PR_TRUE);
00628       CallQueryInterface(newCaption, aValue);
00629     }
00630   }
00631 
00632   return NS_OK;
00633 }
00634 
00635 NS_IMETHODIMP
00636 nsHTMLTableElement::DeleteCaption()
00637 {
00638   nsCOMPtr<nsIDOMHTMLTableCaptionElement> childToDelete;
00639   nsresult rv = GetCaption(getter_AddRefs(childToDelete));
00640 
00641   if ((NS_SUCCEEDED(rv)) && childToDelete) {
00642     nsCOMPtr<nsIDOMNode> resultingChild;
00643     RemoveChild(childToDelete, getter_AddRefs(resultingChild));
00644   }
00645 
00646   return NS_OK;
00647 }
00648 
00649 NS_IMETHODIMP
00650 nsHTMLTableElement::InsertRow(PRInt32 aIndex, nsIDOMHTMLElement** aValue)
00651 {
00652   /* get the ref row at aIndex
00653      if there is one, 
00654        get it's parent
00655        insert the new row just before the ref row
00656      else
00657        get the first row group
00658        insert the new row as its first child
00659   */
00660   *aValue = nsnull;
00661 
00662   if (aIndex < -1) {
00663     return NS_ERROR_DOM_INDEX_SIZE_ERR;
00664   }
00665 
00666   nsresult rv;
00667 
00668   nsCOMPtr<nsIDOMHTMLCollection> rows;
00669   GetRows(getter_AddRefs(rows));
00670 
00671   PRUint32 rowCount;
00672   rows->GetLength(&rowCount);
00673 
00674   if ((PRUint32)aIndex > rowCount && aIndex != -1) {
00675     return NS_ERROR_DOM_INDEX_SIZE_ERR;
00676   }
00677 
00678   // use local variable refIndex so we can remember original aIndex
00679   PRUint32 refIndex = (PRUint32)aIndex;
00680 
00681   if (rowCount > 0) {
00682     if (refIndex == rowCount || aIndex == -1) {
00683       // we set refIndex to the last row so we can get the last row's
00684       // parent we then do an AppendChild below if (rowCount<aIndex)
00685 
00686       refIndex = rowCount - 1;
00687     }
00688 
00689     nsCOMPtr<nsIDOMNode> refRow;
00690     rows->Item(refIndex, getter_AddRefs(refRow));
00691 
00692     nsCOMPtr<nsIDOMNode> parent;
00693 
00694     refRow->GetParentNode(getter_AddRefs(parent));
00695     // create the row
00696     nsCOMPtr<nsINodeInfo> nodeInfo;
00697     nsContentUtils::NameChanged(mNodeInfo, nsHTMLAtoms::tr,
00698                                 getter_AddRefs(nodeInfo));
00699 
00700     nsCOMPtr<nsIContent> newRow = NS_NewHTMLTableRowElement(nodeInfo);
00701 
00702     if (newRow) {
00703       nsCOMPtr<nsIDOMNode> newRowNode(do_QueryInterface(newRow));
00704       nsCOMPtr<nsIDOMNode> retChild;
00705 
00706       // If index is -1 or equal to the number of rows, the new row
00707       // is appended.
00708       if (aIndex == -1 || PRUint32(aIndex) == rowCount) {
00709         rv = parent->AppendChild(newRowNode, getter_AddRefs(retChild));
00710       }
00711       else
00712       {
00713         // insert the new row before the reference row we found above
00714         rv = parent->InsertBefore(newRowNode, refRow,
00715                                   getter_AddRefs(retChild));
00716       }
00717 
00718       if (retChild) {
00719         CallQueryInterface(retChild, aValue);
00720       }
00721     }
00722   }
00723   else
00724   { // the row count was 0, so 
00725     // find the first row group and insert there as first child
00726     nsCOMPtr<nsIDOMNode> rowGroup;
00727 
00728     PRInt32 namespaceID = mNodeInfo->NamespaceID();
00729     PRUint32 childCount = GetChildCount();
00730     for (PRUint32 i = 0; i < childCount; ++i) {
00731       nsIContent* child = GetChildAt(i);
00732       nsINodeInfo* childInfo = child->GetNodeInfo();
00733       if (childInfo &&
00734           (childInfo->Equals(nsHTMLAtoms::thead, namespaceID) ||
00735            childInfo->Equals(nsHTMLAtoms::tbody, namespaceID) ||
00736            childInfo->Equals(nsHTMLAtoms::tfoot, namespaceID))) {
00737         rowGroup = do_QueryInterface(child);
00738         NS_ASSERTION(rowGroup, "HTML node did not QI to nsIDOMNode");
00739         break;
00740       }
00741     }
00742 
00743     if (!rowGroup) { // need to create a TBODY
00744       nsCOMPtr<nsINodeInfo> nodeInfo;
00745       nsContentUtils::NameChanged(mNodeInfo, nsHTMLAtoms::tbody,
00746                                   getter_AddRefs(nodeInfo));
00747 
00748       nsCOMPtr<nsIContent> newRowGroup =
00749         NS_NewHTMLTableSectionElement(nodeInfo);
00750 
00751       if (newRowGroup) {
00752         rv = AppendChildTo(newRowGroup, PR_TRUE);
00753 
00754         rowGroup = do_QueryInterface(newRowGroup);
00755       }
00756     }
00757 
00758     if (rowGroup) {
00759       nsCOMPtr<nsINodeInfo> nodeInfo;
00760       nsContentUtils::NameChanged(mNodeInfo, nsHTMLAtoms::tr,
00761                                   getter_AddRefs(nodeInfo));
00762 
00763       nsCOMPtr<nsIContent> newRow = NS_NewHTMLTableRowElement(nodeInfo);
00764       if (newRow) {
00765         nsCOMPtr<nsIDOMNode> firstRow;
00766 
00767         nsCOMPtr<nsIDOMHTMLTableSectionElement> section =
00768           do_QueryInterface(rowGroup);
00769 
00770         if (section) {
00771           nsCOMPtr<nsIDOMHTMLCollection> rows;
00772           section->GetRows(getter_AddRefs(rows));
00773           if (rows) {
00774             rows->Item(0, getter_AddRefs(firstRow));
00775           }
00776         }
00777         
00778         nsCOMPtr<nsIDOMNode> retNode, newRowNode(do_QueryInterface(newRow));
00779 
00780         rowGroup->InsertBefore(newRowNode, firstRow, getter_AddRefs(retNode));
00781 
00782         if (retNode) {
00783           CallQueryInterface(retNode, aValue);
00784         }
00785       }
00786     }
00787   }
00788 
00789   return NS_OK;
00790 }
00791 
00792 NS_IMETHODIMP
00793 nsHTMLTableElement::DeleteRow(PRInt32 aValue)
00794 {
00795   if (aValue < -1) {
00796     return NS_ERROR_DOM_INDEX_SIZE_ERR;
00797   }
00798 
00799   nsCOMPtr<nsIDOMHTMLCollection> rows;
00800   GetRows(getter_AddRefs(rows));
00801 
00802   nsresult rv;
00803   PRUint32 refIndex;
00804   if (aValue == -1) {
00805     rv = rows->GetLength(&refIndex);
00806     NS_ENSURE_SUCCESS(rv, rv);
00807 
00808     if (refIndex == 0) {
00809       return NS_OK;
00810     }
00811 
00812     --refIndex;
00813   }
00814   else {
00815     refIndex = (PRUint32)aValue;
00816   }
00817 
00818   nsCOMPtr<nsIDOMNode> row;
00819   rv = rows->Item(refIndex, getter_AddRefs(row));
00820   NS_ENSURE_SUCCESS(rv, rv);
00821 
00822   if (!row) {
00823     return NS_ERROR_DOM_INDEX_SIZE_ERR;
00824   }
00825 
00826   nsCOMPtr<nsIDOMNode> parent;
00827   row->GetParentNode(getter_AddRefs(parent));
00828   NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
00829 
00830   nsCOMPtr<nsIDOMNode> deleted_row;
00831   return parent->RemoveChild(row, getter_AddRefs(deleted_row));
00832 }
00833 
00834 static const nsAttrValue::EnumTable kFrameTable[] = {
00835   { "void",   NS_STYLE_TABLE_FRAME_NONE },
00836   { "above",  NS_STYLE_TABLE_FRAME_ABOVE },
00837   { "below",  NS_STYLE_TABLE_FRAME_BELOW },
00838   { "hsides", NS_STYLE_TABLE_FRAME_HSIDES },
00839   { "lhs",    NS_STYLE_TABLE_FRAME_LEFT },
00840   { "rhs",    NS_STYLE_TABLE_FRAME_RIGHT },
00841   { "vsides", NS_STYLE_TABLE_FRAME_VSIDES },
00842   { "box",    NS_STYLE_TABLE_FRAME_BOX },
00843   { "border", NS_STYLE_TABLE_FRAME_BORDER },
00844   { 0 }
00845 };
00846 
00847 static const nsAttrValue::EnumTable kRulesTable[] = {
00848   { "none",   NS_STYLE_TABLE_RULES_NONE },
00849   { "groups", NS_STYLE_TABLE_RULES_GROUPS },
00850   { "rows",   NS_STYLE_TABLE_RULES_ROWS },
00851   { "cols",   NS_STYLE_TABLE_RULES_COLS },
00852   { "all",    NS_STYLE_TABLE_RULES_ALL },
00853   { 0 }
00854 };
00855 
00856 static const nsAttrValue::EnumTable kLayoutTable[] = {
00857   { "auto",   NS_STYLE_TABLE_LAYOUT_AUTO },
00858   { "fixed",  NS_STYLE_TABLE_LAYOUT_FIXED },
00859   { 0 }
00860 };
00861 
00862 
00863 PRBool
00864 nsHTMLTableElement::ParseAttribute(nsIAtom* aAttribute,
00865                                    const nsAString& aValue,
00866                                    nsAttrValue& aResult)
00867 {
00868   /* ignore summary, just a string */
00869   if (aAttribute == nsHTMLAtoms::cellspacing ||
00870       aAttribute == nsHTMLAtoms::cellpadding) {
00871     return aResult.ParseSpecialIntValue(aValue, PR_TRUE, PR_FALSE);
00872   }
00873   if (aAttribute == nsHTMLAtoms::cols) {
00874     return aResult.ParseIntWithBounds(aValue, 0);
00875   }
00876   if (aAttribute == nsHTMLAtoms::border) {
00877     if (!aResult.ParseIntWithBounds(aValue, 0)) {
00878       // XXX this should really be NavQuirks only to allow non numeric value
00879       aResult.SetTo(1);
00880     }
00881 
00882     return PR_TRUE;
00883   }
00884   if (aAttribute == nsHTMLAtoms::height) {
00885     return aResult.ParseSpecialIntValue(aValue, PR_TRUE, PR_FALSE);
00886   }
00887   if (aAttribute == nsHTMLAtoms::width) {
00888     if (aResult.ParseSpecialIntValue(aValue, PR_TRUE, PR_FALSE)) {
00889       // treat 0 width as auto
00890       nsAttrValue::ValueType type = aResult.Type();
00891       if ((type == nsAttrValue::eInteger && aResult.GetIntegerValue() == 0) ||
00892           (type == nsAttrValue::ePercent && aResult.GetPercentValue() == 0.0f)) {
00893         return PR_FALSE;
00894       }
00895     }
00896     return PR_TRUE;
00897   }
00898   if (aAttribute == nsHTMLAtoms::align) {
00899     return ParseTableHAlignValue(aValue, aResult);
00900   }
00901   if (aAttribute == nsHTMLAtoms::bgcolor ||
00902            aAttribute == nsHTMLAtoms::bordercolor) {
00903     return aResult.ParseColor(aValue, GetOwnerDoc());
00904   }
00905   if (aAttribute == nsHTMLAtoms::frame) {
00906     return aResult.ParseEnumValue(aValue, kFrameTable);
00907   }
00908   if (aAttribute == nsHTMLAtoms::layout) {
00909     return aResult.ParseEnumValue(aValue, kLayoutTable);
00910   }
00911   if (aAttribute == nsHTMLAtoms::rules) {
00912     return aResult.ParseEnumValue(aValue, kRulesTable);
00913   }
00914   if (aAttribute == nsHTMLAtoms::hspace ||
00915            aAttribute == nsHTMLAtoms::vspace) {
00916     return aResult.ParseIntWithBounds(aValue, 0);
00917   }
00918 
00919   return nsGenericHTMLElement::ParseAttribute(aAttribute, aValue, aResult);
00920 }
00921 
00922 static void 
00923 MapTableFrameInto(const nsMappedAttributes* aAttributes,
00924                   nsRuleData* aData, PRUint8 aBorderStyle)
00925 {
00926   if (!aData->mMarginData)
00927     return;
00928 
00929   // set up defaults
00930   if (aData->mMarginData->mBorderStyle.mLeft.GetUnit() == eCSSUnit_Null)
00931     aData->mMarginData->mBorderStyle.mLeft.SetIntValue(aBorderStyle, eCSSUnit_Enumerated);
00932   if (aData->mMarginData->mBorderStyle.mRight.GetUnit() == eCSSUnit_Null)
00933     aData->mMarginData->mBorderStyle.mRight.SetIntValue(aBorderStyle, eCSSUnit_Enumerated);
00934   if (aData->mMarginData->mBorderStyle.mTop.GetUnit() == eCSSUnit_Null)
00935     aData->mMarginData->mBorderStyle.mTop.SetIntValue(aBorderStyle, eCSSUnit_Enumerated);
00936   if (aData->mMarginData->mBorderStyle.mBottom.GetUnit() == eCSSUnit_Null)
00937     aData->mMarginData->mBorderStyle.mBottom.SetIntValue(aBorderStyle, eCSSUnit_Enumerated);
00938 
00939   // 0 out the sides that we want to hide based on the frame attribute
00940   const nsAttrValue* frameValue = aAttributes->GetAttr(nsHTMLAtoms::frame);
00941 
00942   if (frameValue && frameValue->Type() == nsAttrValue::eEnum) {
00943     // adjust the border style based on the value of frame
00944     switch (frameValue->GetEnumValue())
00945     {
00946     case NS_STYLE_TABLE_FRAME_NONE:
00947       aData->mMarginData->mBorderStyle.mLeft.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00948       aData->mMarginData->mBorderStyle.mRight.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00949       aData->mMarginData->mBorderStyle.mTop.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00950       aData->mMarginData->mBorderStyle.mBottom.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00951       break;
00952     case NS_STYLE_TABLE_FRAME_ABOVE:
00953       aData->mMarginData->mBorderStyle.mLeft.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00954       aData->mMarginData->mBorderStyle.mRight.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00955       aData->mMarginData->mBorderStyle.mBottom.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00956       break;
00957     case NS_STYLE_TABLE_FRAME_BELOW: 
00958       aData->mMarginData->mBorderStyle.mLeft.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00959       aData->mMarginData->mBorderStyle.mRight.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00960       aData->mMarginData->mBorderStyle.mTop.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00961       break;
00962     case NS_STYLE_TABLE_FRAME_HSIDES:
00963       aData->mMarginData->mBorderStyle.mLeft.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00964       aData->mMarginData->mBorderStyle.mRight.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00965       break;
00966     case NS_STYLE_TABLE_FRAME_LEFT:
00967       aData->mMarginData->mBorderStyle.mRight.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00968       aData->mMarginData->mBorderStyle.mTop.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00969       aData->mMarginData->mBorderStyle.mBottom.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00970       break;
00971     case NS_STYLE_TABLE_FRAME_RIGHT:
00972       aData->mMarginData->mBorderStyle.mLeft.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00973       aData->mMarginData->mBorderStyle.mTop.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00974       aData->mMarginData->mBorderStyle.mBottom.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00975       break;
00976     case NS_STYLE_TABLE_FRAME_VSIDES:
00977       aData->mMarginData->mBorderStyle.mTop.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00978       aData->mMarginData->mBorderStyle.mBottom.SetIntValue(NS_STYLE_BORDER_STYLE_NONE, eCSSUnit_Enumerated);
00979       break;
00980     // BOX and BORDER are ignored, the caller has already set all the border sides
00981     // any illegal value is also ignored
00982     }
00983   }
00984 }
00985 
00986 static void 
00987 MapTableBorderInto(const nsMappedAttributes* aAttributes,
00988                    nsRuleData* aData, PRUint8 aBorderStyle)
00989 {
00990   const nsAttrValue* borderValue = aAttributes->GetAttr(nsHTMLAtoms::border);
00991   if (!borderValue && !aAttributes->GetAttr(nsHTMLAtoms::frame))
00992     return;
00993 
00994   // the absence of "border" with the presence of "frame" implies
00995   // border = 1 pixel
00996   PRInt32 borderThickness = 1;
00997 
00998   if (borderValue && borderValue->Type() == nsAttrValue::eInteger)
00999     borderThickness = borderValue->GetIntegerValue();
01000 
01001   if (aData->mTableData) {
01002     if (0 != borderThickness) {
01003       // border != 0 implies rules=all and frame=border
01004       aData->mTableData->mRules.SetIntValue(NS_STYLE_TABLE_RULES_ALL, eCSSUnit_Enumerated);
01005       aData->mTableData->mFrame.SetIntValue(NS_STYLE_TABLE_FRAME_BORDER, eCSSUnit_Enumerated);
01006     }
01007     else {
01008       // border = 0 implies rules=none and frame=void
01009       aData->mTableData->mRules.SetIntValue(NS_STYLE_TABLE_RULES_NONE, eCSSUnit_Enumerated);
01010       aData->mTableData->mFrame.SetIntValue(NS_STYLE_TABLE_FRAME_NONE, eCSSUnit_Enumerated);
01011     }
01012   }
01013 
01014   if (aData->mMarginData) {
01015     // by default, set all border sides to the specified width
01016     if (aData->mMarginData->mBorderWidth.mLeft.GetUnit() == eCSSUnit_Null)
01017       aData->mMarginData->mBorderWidth.mLeft.SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
01018     if (aData->mMarginData->mBorderWidth.mRight.GetUnit() == eCSSUnit_Null)
01019       aData->mMarginData->mBorderWidth.mRight.SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
01020     if (aData->mMarginData->mBorderWidth.mTop.GetUnit() == eCSSUnit_Null)
01021       aData->mMarginData->mBorderWidth.mTop .SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
01022     if (aData->mMarginData->mBorderWidth.mBottom.GetUnit() == eCSSUnit_Null)
01023       aData->mMarginData->mBorderWidth.mBottom.SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
01024 
01025     // now account for the frame attribute
01026     MapTableFrameInto(aAttributes, aData, aBorderStyle);
01027   }
01028 }
01029 
01030 static void
01031 MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
01032                       nsRuleData* aData)
01033 {
01034   // XXX Bug 211636:  This function is used by a single style rule
01035   // that's used to match two different type of elements -- tables, and
01036   // table cells.  (nsHTMLTableCellElement overrides
01037   // WalkContentStyleRules so that this happens.)  This violates the
01038   // nsIStyleRule contract, since it's the same style rule object doing
01039   // the mapping in two different ways.  It's also incorrect since it's
01040   // testing the display type of the style context rather than checking
01041   // which *element* it's matching (style rules should not stop matching
01042   // when the display type is changed).
01043 
01044   nsCompatibility mode = aData->mPresContext->CompatibilityMode();
01045 
01046   if (aData->mSID == eStyleStruct_TableBorder) {
01047     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01048     if (readDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL) {
01049       // cellspacing 
01050       const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::cellspacing);
01051       if (value && value->Type() == nsAttrValue::eInteger) {
01052         if (aData->mTableData->mBorderSpacing.mXValue.GetUnit() == eCSSUnit_Null)
01053           aData->mTableData->mBorderSpacing.mXValue.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
01054         if (aData->mTableData->mBorderSpacing.mYValue.GetUnit() == eCSSUnit_Null)
01055           aData->mTableData->mBorderSpacing.mYValue.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
01056       }
01057       else if (value && value->Type() == nsAttrValue::ePercent && eCompatibility_NavQuirks == mode) {
01058         // in quirks mode, treat a % cellspacing value a pixel value.
01059         if (aData->mTableData->mBorderSpacing.mXValue.GetUnit() == eCSSUnit_Null)
01060           aData->mTableData->mBorderSpacing.mXValue.SetFloatValue(100.0f * value->GetPercentValue(), eCSSUnit_Pixel);
01061         if (aData->mTableData->mBorderSpacing.mYValue.GetUnit() == eCSSUnit_Null)
01062           aData->mTableData->mBorderSpacing.mYValue.SetFloatValue(100.0f * value->GetPercentValue(), eCSSUnit_Pixel);
01063       }
01064     }
01065   } 
01066   else if (aData->mSID == eStyleStruct_Table) {
01067     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01068     if (readDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL) {
01069       MapTableBorderInto(aAttributes, aData, 0);
01070 
01071       const nsAttrValue* value;
01072       // layout
01073       if (aData->mTableData->mLayout.GetUnit() == eCSSUnit_Null) {
01074         value = aAttributes->GetAttr(nsHTMLAtoms::layout);
01075         if (value && value->Type() == nsAttrValue::eEnum)
01076           aData->mTableData->mLayout.SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
01077       }
01078       
01079       // cols
01080       value = aAttributes->GetAttr(nsHTMLAtoms::cols);
01081       if (value) {
01082         if (value->Type() == nsAttrValue::eInteger) 
01083           aData->mTableData->mCols.SetIntValue(value->GetIntegerValue(), eCSSUnit_Integer);
01084         else // COLS had no value, so it refers to all columns
01085           aData->mTableData->mCols.SetIntValue(NS_STYLE_TABLE_COLS_ALL, eCSSUnit_Enumerated);
01086       }
01087 
01088       // rules
01089       value = aAttributes->GetAttr(nsHTMLAtoms::rules);
01090       if (value && value->Type() == nsAttrValue::eEnum)
01091         aData->mTableData->mRules.SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
01092     }
01093   }
01094   else if (aData->mSID == eStyleStruct_Margin) {
01095     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01096   
01097     if (readDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL) {
01098       // align; Check for enumerated type (it may be another type if
01099       // illegal)
01100       const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::align);
01101 
01102       if (value && value->Type() == nsAttrValue::eEnum) {
01103         if (value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_CENTER ||
01104             value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) {
01105           nsCSSRect& margin = aData->mMarginData->mMargin;
01106           if (margin.mLeft.GetUnit() == eCSSUnit_Null)
01107             margin.mLeft.SetAutoValue();
01108           if (margin.mRight.GetUnit() == eCSSUnit_Null)
01109             margin.mRight.SetAutoValue();
01110         }
01111       }
01112 
01113       // hspace is mapped into left and right margin, 
01114       // vspace is mapped into top and bottom margins
01115       // - *** Quirks Mode only ***
01116       if (eCompatibility_NavQuirks == mode) {
01117         value = aAttributes->GetAttr(nsHTMLAtoms::hspace);
01118 
01119         if (value && value->Type() == nsAttrValue::eInteger) {
01120           nsCSSRect& margin = aData->mMarginData->mMargin;
01121           if (margin.mLeft.GetUnit() == eCSSUnit_Null)
01122             margin.mLeft.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); 
01123           if (margin.mRight.GetUnit() == eCSSUnit_Null)
01124             margin.mRight.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
01125         }
01126 
01127         value = aAttributes->GetAttr(nsHTMLAtoms::vspace);
01128 
01129         if (value && value->Type() == nsAttrValue::eInteger) {
01130           nsCSSRect& margin = aData->mMarginData->mMargin;
01131           if (margin.mTop.GetUnit() == eCSSUnit_Null)
01132             margin.mTop.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); 
01133           if (margin.mBottom.GetUnit() == eCSSUnit_Null)
01134             margin.mBottom.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); 
01135         }
01136       }
01137     }
01138   }
01139   else if (aData->mSID == eStyleStruct_Padding) {
01140     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01141     if (readDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
01142       const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::cellpadding);
01143       if (value) {
01144         nsAttrValue::ValueType valueType = value->Type();
01145         if (valueType == nsAttrValue::eInteger || valueType == nsAttrValue::ePercent) {
01146           // We have cellpadding.  This will override our padding values if we don't
01147           // have any set.
01148           nsCSSValue padVal;
01149           if (valueType == nsAttrValue::eInteger)
01150             padVal.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
01151           else {
01152             // when we support % cellpadding in standard mode, uncomment the following
01153             float pctVal = value->GetPercentValue();
01154             //if (eCompatibility_NavQuirks == mode) {
01155               // in quirks mode treat a pct cellpadding value as a pixel value
01156               padVal.SetFloatValue(100.0f * pctVal, eCSSUnit_Pixel);
01157             //}
01158             //else {
01159             //  padVal.SetPercentValue(pctVal);
01160             //}
01161           }
01162           if (aData->mMarginData->mPadding.mLeft.GetUnit() == eCSSUnit_Null)
01163             aData->mMarginData->mPadding.mLeft = padVal;
01164           if (aData->mMarginData->mPadding.mRight.GetUnit() == eCSSUnit_Null)
01165             aData->mMarginData->mPadding.mRight = padVal;
01166           if (aData->mMarginData->mPadding.mTop.GetUnit() == eCSSUnit_Null)
01167             aData->mMarginData->mPadding.mTop = padVal;
01168           if (aData->mMarginData->mPadding.mBottom.GetUnit() == eCSSUnit_Null)
01169             aData->mMarginData->mPadding.mBottom = padVal;
01170         }
01171       }
01172     }
01173   }
01174   else if (aData->mSID == eStyleStruct_Position) {
01175     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01176   
01177     if (readDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL) {
01178       // width: value
01179       if (aData->mPositionData->mWidth.GetUnit() == eCSSUnit_Null) {
01180         const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::width);
01181         if (value && value->Type() == nsAttrValue::eInteger) 
01182           aData->mPositionData->mWidth.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
01183         else if (value && value->Type() == nsAttrValue::ePercent)
01184           aData->mPositionData->mWidth.SetPercentValue(value->GetPercentValue());
01185       }
01186 
01187       // height: value
01188       if (aData->mPositionData->mHeight.GetUnit() == eCSSUnit_Null) {
01189         const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::height);
01190         if (value && value->Type() == nsAttrValue::eInteger) 
01191           aData->mPositionData->mHeight.SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
01192         else if (value && value->Type() == nsAttrValue::ePercent)
01193           aData->mPositionData->mHeight.SetPercentValue(value->GetPercentValue()); 
01194       }
01195     }
01196   }
01197   else if (aData->mSID == eStyleStruct_Visibility) {
01198     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01199   
01200     if (readDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL)
01201       nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
01202   }
01203   else if (aData->mSID == eStyleStruct_Border) {
01204     if (!aData->mStyleContext) return;
01205     const nsStyleTableBorder* tableStyle = aData->mStyleContext->GetStyleTableBorder();
01206     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01207     if (readDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
01208       if (NS_STYLE_BORDER_SEPARATE == tableStyle->mBorderCollapse) {
01209         // Set the cell's border from the table in the separate border
01210         // model. If there is a border on the table, then the mapping to
01211         // rules=all will take care of borders in the collapsing model.
01212         // But if rules="none", we don't want to do this.
01213         const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::border);
01214         const nsAttrValue* rulesValue = aAttributes->GetAttr(nsHTMLAtoms::rules);
01215         if ((!rulesValue || rulesValue->Type() != nsAttrValue::eEnum ||
01216              rulesValue->GetEnumValue() != NS_STYLE_TABLE_RULES_NONE) &&
01217             value &&
01218             ((value->Type() == nsAttrValue::eInteger &&
01219               value->GetIntegerValue() > 0) ||
01220              value->IsEmptyString())) {
01221           if (aData->mMarginData->mBorderWidth.mLeft.GetUnit() == eCSSUnit_Null)
01222             aData->mMarginData->mBorderWidth.mLeft.SetFloatValue(1.0f, eCSSUnit_Pixel);
01223           if (aData->mMarginData->mBorderWidth.mRight.GetUnit() == eCSSUnit_Null)
01224             aData->mMarginData->mBorderWidth.mRight.SetFloatValue(1.0f, eCSSUnit_Pixel);
01225           if (aData->mMarginData->mBorderWidth.mTop.GetUnit() == eCSSUnit_Null)
01226             aData->mMarginData->mBorderWidth.mTop.SetFloatValue(1.0f, eCSSUnit_Pixel);
01227           if (aData->mMarginData->mBorderWidth.mBottom.GetUnit() == eCSSUnit_Null)
01228             aData->mMarginData->mBorderWidth.mBottom.SetFloatValue(1.0f, eCSSUnit_Pixel);
01229 
01230           PRUint8 borderStyle = (eCompatibility_NavQuirks == mode) 
01231                                 ? NS_STYLE_BORDER_STYLE_BG_INSET : NS_STYLE_BORDER_STYLE_INSET;
01232           // BG_INSET results in a border color based on background colors
01233           // used for NavQuirks only...
01234 
01235           if (aData->mMarginData->mBorderStyle.mLeft.GetUnit() == eCSSUnit_Null)
01236             aData->mMarginData->mBorderStyle.mLeft.SetIntValue(borderStyle, eCSSUnit_Enumerated);
01237           if (aData->mMarginData->mBorderStyle.mRight.GetUnit() == eCSSUnit_Null)
01238             aData->mMarginData->mBorderStyle.mRight.SetIntValue(borderStyle, eCSSUnit_Enumerated);
01239           if (aData->mMarginData->mBorderStyle.mTop.GetUnit() == eCSSUnit_Null)
01240             aData->mMarginData->mBorderStyle.mTop.SetIntValue(borderStyle, eCSSUnit_Enumerated);
01241           if (aData->mMarginData->mBorderStyle.mBottom.GetUnit() == eCSSUnit_Null)
01242             aData->mMarginData->mBorderStyle.mBottom.SetIntValue(borderStyle, eCSSUnit_Enumerated);
01243         }
01244       }
01245     }
01246     else {
01247       // default border style is the Nav4.6 extension which uses the
01248       // background color as the basis of the outset border. If the
01249       // table has a transparent background then it finds the closest
01250       // ancestor that has a non-transparent
01251       // background. NS_STYLE_BORDER_OUTSET uses the border color of
01252       // the table and if that is not set, then it uses the color.
01253 
01254       PRUint8 borderStyle = (eCompatibility_NavQuirks == mode) 
01255                             ? NS_STYLE_BORDER_STYLE_BG_OUTSET :
01256                               NS_STYLE_BORDER_STYLE_OUTSET;
01257       // bordercolor
01258       const nsAttrValue* value = aAttributes->GetAttr(nsHTMLAtoms::bordercolor);
01259       nscolor color;
01260       if (value && value->GetColorValue(color)) {
01261         if (aData->mMarginData->mBorderColor.mLeft.GetUnit() == eCSSUnit_Null)
01262           aData->mMarginData->mBorderColor.mLeft.SetColorValue(color);
01263         if (aData->mMarginData->mBorderColor.mRight.GetUnit() == eCSSUnit_Null)
01264           aData->mMarginData->mBorderColor.mRight.SetColorValue(color);
01265         if (aData->mMarginData->mBorderColor.mTop.GetUnit() == eCSSUnit_Null)
01266           aData->mMarginData->mBorderColor.mTop.SetColorValue(color);
01267         if (aData->mMarginData->mBorderColor.mBottom.GetUnit() == eCSSUnit_Null)
01268           aData->mMarginData->mBorderColor.mBottom.SetColorValue(color);
01269 
01270         borderStyle = NS_STYLE_BORDER_STYLE_OUTSET; // use css outset
01271       }
01272       else if (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse) {
01273         // make the color grey
01274         nscolor color = NS_RGB(80, 80, 80);
01275         if (aData->mMarginData->mBorderColor.mLeft.GetUnit() == eCSSUnit_Null)
01276           aData->mMarginData->mBorderColor.mLeft.SetColorValue(color);
01277         if (aData->mMarginData->mBorderColor.mRight.GetUnit() == eCSSUnit_Null)
01278           aData->mMarginData->mBorderColor.mRight.SetColorValue(color);
01279         if (aData->mMarginData->mBorderColor.mTop.GetUnit() == eCSSUnit_Null)
01280           aData->mMarginData->mBorderColor.mTop.SetColorValue(color);
01281         if (aData->mMarginData->mBorderColor.mBottom.GetUnit() == eCSSUnit_Null)
01282           aData->mMarginData->mBorderColor.mBottom.SetColorValue(color);
01283       }
01284 
01285       // border and frame
01286       MapTableBorderInto(aAttributes, aData, borderStyle);
01287     }
01288   }
01289   else if (aData->mSID == eStyleStruct_Background) {
01290     const nsStyleDisplay* readDisplay = aData->mStyleContext->GetStyleDisplay();
01291   
01292     if (readDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CELL)
01293       nsGenericHTMLElement::MapBackgroundAttributesInto(aAttributes, aData);
01294   }
01295 }
01296 
01297 NS_IMETHODIMP_(PRBool)
01298 nsHTMLTableElement::IsAttributeMapped(const nsIAtom* aAttribute) const
01299 {
01300   static const MappedAttributeEntry attributes[] = {
01301     { &nsHTMLAtoms::layout },
01302     { &nsHTMLAtoms::cellpadding },
01303     { &nsHTMLAtoms::cellspacing },
01304     { &nsHTMLAtoms::cols },
01305     { &nsHTMLAtoms::border },
01306     { &nsHTMLAtoms::frame },
01307     { &nsHTMLAtoms::width },
01308     { &nsHTMLAtoms::height },
01309     { &nsHTMLAtoms::hspace },
01310     { &nsHTMLAtoms::vspace },
01311     
01312     { &nsHTMLAtoms::bordercolor },
01313     
01314     { &nsHTMLAtoms::align },
01315     { &nsHTMLAtoms::rules },
01316     { nsnull }
01317   };
01318 
01319   static const MappedAttributeEntry* const map[] = {
01320     attributes,
01321     sCommonAttributeMap,
01322     sBackgroundAttributeMap,
01323   };
01324 
01325   return FindAttributeDependence(aAttribute, map, NS_ARRAY_LENGTH(map));
01326 }
01327 
01328 nsMapRuleToAttributesFunc
01329 nsHTMLTableElement::GetAttributeMappingFunction() const
01330 {
01331   return &MapAttributesIntoRule;
01332 }