HTMLTableElement.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/HTMLTableElement.h"
  6. #include "nsAttrValueInlines.h"
  7. #include "nsRuleData.h"
  8. #include "nsHTMLStyleSheet.h"
  9. #include "nsMappedAttributes.h"
  10. #include "mozilla/dom/HTMLCollectionBinding.h"
  11. #include "mozilla/dom/HTMLTableElementBinding.h"
  12. #include "nsContentUtils.h"
  13. #include "jsfriendapi.h"
  14. NS_IMPL_NS_NEW_HTML_ELEMENT(Table)
  15. namespace mozilla {
  16. namespace dom {
  17. /* ------------------------------ TableRowsCollection -------------------------------- */
  18. /**
  19. * This class provides a late-bound collection of rows in a table.
  20. * mParent is NOT ref-counted to avoid circular references
  21. */
  22. class TableRowsCollection final : public nsIHTMLCollection,
  23. public nsWrapperCache
  24. {
  25. public:
  26. explicit TableRowsCollection(HTMLTableElement* aParent);
  27. NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  28. NS_DECL_NSIDOMHTMLCOLLECTION
  29. virtual Element* GetElementAt(uint32_t aIndex) override;
  30. virtual nsINode* GetParentObject() override
  31. {
  32. return mParent;
  33. }
  34. virtual Element*
  35. GetFirstNamedElement(const nsAString& aName, bool& aFound) override;
  36. virtual void GetSupportedNames(nsTArray<nsString>& aNames) override;
  37. NS_IMETHOD ParentDestroyed();
  38. NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TableRowsCollection)
  39. // nsWrapperCache
  40. using nsWrapperCache::GetWrapperPreserveColor;
  41. virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
  42. protected:
  43. virtual ~TableRowsCollection();
  44. virtual JSObject* GetWrapperPreserveColorInternal() override
  45. {
  46. return nsWrapperCache::GetWrapperPreserveColor();
  47. }
  48. // Those rows that are not in table sections
  49. HTMLTableElement* mParent;
  50. };
  51. TableRowsCollection::TableRowsCollection(HTMLTableElement *aParent)
  52. : mParent(aParent)
  53. {
  54. }
  55. TableRowsCollection::~TableRowsCollection()
  56. {
  57. // we do NOT have a ref-counted reference to mParent, so do NOT
  58. // release it! this is to avoid circular references. The
  59. // instantiator who provided mParent is responsible for managing our
  60. // reference for us.
  61. }
  62. JSObject*
  63. TableRowsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  64. {
  65. return HTMLCollectionBinding::Wrap(aCx, this, aGivenProto);
  66. }
  67. NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(TableRowsCollection)
  68. NS_IMPL_CYCLE_COLLECTING_ADDREF(TableRowsCollection)
  69. NS_IMPL_CYCLE_COLLECTING_RELEASE(TableRowsCollection)
  70. NS_INTERFACE_TABLE_HEAD(TableRowsCollection)
  71. NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
  72. NS_INTERFACE_TABLE(TableRowsCollection, nsIHTMLCollection,
  73. nsIDOMHTMLCollection)
  74. NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(TableRowsCollection)
  75. NS_INTERFACE_MAP_END
  76. // Macro that can be used to avoid copy/pasting code to iterate over the
  77. // rowgroups. _code should be the code to execute for each rowgroup. The
  78. // rowgroup's rows will be in the nsIDOMHTMLCollection* named "rows".
  79. // _trCode should be the code to execute for each tr row. Note that
  80. // this may be null at any time. This macro assumes an nsresult named
  81. // |rv| is in scope.
  82. #define DO_FOR_EACH_BY_ORDER(_code, _trCode) \
  83. do { \
  84. if (mParent) { \
  85. HTMLTableSectionElement* rowGroup; \
  86. nsIHTMLCollection* rows; \
  87. /* THead */ \
  88. for (nsIContent* _node = mParent->nsINode::GetFirstChild(); \
  89. _node; _node = _node->GetNextSibling()) { \
  90. if (_node->IsHTMLElement(nsGkAtoms::thead)) { \
  91. rowGroup = static_cast<HTMLTableSectionElement*>(_node);\
  92. rows = rowGroup->Rows(); \
  93. do { /* gives scoping */ \
  94. _code \
  95. } while (0); \
  96. } \
  97. } \
  98. /* TBodies */ \
  99. for (nsIContent* _node = mParent->nsINode::GetFirstChild(); \
  100. _node; _node = _node->GetNextSibling()) { \
  101. if (_node->IsHTMLElement(nsGkAtoms::tr)) { \
  102. do { \
  103. _trCode \
  104. } while (0); \
  105. } else if (_node->IsHTMLElement(nsGkAtoms::tbody)) { \
  106. rowGroup = static_cast<HTMLTableSectionElement*>(_node); \
  107. rows = rowGroup->Rows(); \
  108. do { /* gives scoping */ \
  109. _code \
  110. } while (0); \
  111. } \
  112. } \
  113. /* TFoot */ \
  114. for (nsIContent* _node = mParent->nsINode::GetFirstChild(); \
  115. _node; _node = _node->GetNextSibling()) { \
  116. if (_node->IsHTMLElement(nsGkAtoms::tfoot)) { \
  117. rowGroup = static_cast<HTMLTableSectionElement*>(_node);\
  118. rows = rowGroup->Rows(); \
  119. do { /* gives scoping */ \
  120. _code \
  121. } while (0); \
  122. } \
  123. } \
  124. } \
  125. } while (0)
  126. static uint32_t
  127. CountRowsInRowGroup(nsIDOMHTMLCollection* rows)
  128. {
  129. uint32_t length = 0;
  130. if (rows) {
  131. rows->GetLength(&length);
  132. }
  133. return length;
  134. }
  135. // we re-count every call. A better implementation would be to set
  136. // ourselves up as an observer of contentAppended, contentInserted,
  137. // and contentDeleted
  138. NS_IMETHODIMP
  139. TableRowsCollection::GetLength(uint32_t* aLength)
  140. {
  141. *aLength=0;
  142. DO_FOR_EACH_BY_ORDER({
  143. *aLength += CountRowsInRowGroup(rows);
  144. }, {
  145. (*aLength)++;
  146. });
  147. return NS_OK;
  148. }
  149. // Returns the item at index aIndex if available. If null is returned,
  150. // then aCount will be set to the number of rows in this row collection.
  151. // Otherwise, the value of aCount is undefined.
  152. static Element*
  153. GetItemOrCountInRowGroup(nsIDOMHTMLCollection* rows,
  154. uint32_t aIndex, uint32_t* aCount)
  155. {
  156. *aCount = 0;
  157. if (rows) {
  158. rows->GetLength(aCount);
  159. if (aIndex < *aCount) {
  160. nsIHTMLCollection* list = static_cast<nsIHTMLCollection*>(rows);
  161. return list->GetElementAt(aIndex);
  162. }
  163. }
  164. return nullptr;
  165. }
  166. Element*
  167. TableRowsCollection::GetElementAt(uint32_t aIndex)
  168. {
  169. DO_FOR_EACH_BY_ORDER({
  170. uint32_t count;
  171. Element* node = GetItemOrCountInRowGroup(rows, aIndex, &count);
  172. if (node) {
  173. return node;
  174. }
  175. NS_ASSERTION(count <= aIndex, "GetItemOrCountInRowGroup screwed up");
  176. aIndex -= count;
  177. },{
  178. if (aIndex == 0) {
  179. return _node->AsElement();
  180. }
  181. aIndex--;
  182. });
  183. return nullptr;
  184. }
  185. NS_IMETHODIMP
  186. TableRowsCollection::Item(uint32_t aIndex, nsIDOMNode** aReturn)
  187. {
  188. nsISupports* node = GetElementAt(aIndex);
  189. if (!node) {
  190. *aReturn = nullptr;
  191. return NS_OK;
  192. }
  193. return CallQueryInterface(node, aReturn);
  194. }
  195. Element*
  196. TableRowsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound)
  197. {
  198. aFound = false;
  199. nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
  200. NS_ENSURE_TRUE(nameAtom, nullptr);
  201. DO_FOR_EACH_BY_ORDER({
  202. Element* item = rows->NamedGetter(aName, aFound);
  203. if (aFound) {
  204. return item;
  205. }
  206. }, {
  207. if (_node->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
  208. nameAtom, eCaseMatters) ||
  209. _node->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
  210. nameAtom, eCaseMatters)) {
  211. aFound = true;
  212. return _node->AsElement();
  213. }
  214. });
  215. return nullptr;
  216. }
  217. void
  218. TableRowsCollection::GetSupportedNames(nsTArray<nsString>& aNames)
  219. {
  220. DO_FOR_EACH_BY_ORDER({
  221. nsTArray<nsString> names;
  222. nsCOMPtr<nsIHTMLCollection> coll = do_QueryInterface(rows);
  223. if (coll) {
  224. coll->GetSupportedNames(names);
  225. for (uint32_t i = 0; i < names.Length(); ++i) {
  226. if (!aNames.Contains(names[i])) {
  227. aNames.AppendElement(names[i]);
  228. }
  229. }
  230. }
  231. }, {
  232. if (_node->HasID()) {
  233. nsIAtom* idAtom = _node->GetID();
  234. MOZ_ASSERT(idAtom != nsGkAtoms::_empty,
  235. "Empty ids don't get atomized");
  236. nsDependentAtomString idStr(idAtom);
  237. if (!aNames.Contains(idStr)) {
  238. aNames.AppendElement(idStr);
  239. }
  240. }
  241. nsGenericHTMLElement* el = nsGenericHTMLElement::FromContent(_node);
  242. if (el) {
  243. const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
  244. if (val && val->Type() == nsAttrValue::eAtom) {
  245. nsIAtom* nameAtom = val->GetAtomValue();
  246. MOZ_ASSERT(nameAtom != nsGkAtoms::_empty,
  247. "Empty names don't get atomized");
  248. nsDependentAtomString nameStr(nameAtom);
  249. if (!aNames.Contains(nameStr)) {
  250. aNames.AppendElement(nameStr);
  251. }
  252. }
  253. }
  254. });
  255. }
  256. NS_IMETHODIMP
  257. TableRowsCollection::NamedItem(const nsAString& aName,
  258. nsIDOMNode** aReturn)
  259. {
  260. bool found;
  261. nsISupports* node = GetFirstNamedElement(aName, found);
  262. if (!node) {
  263. *aReturn = nullptr;
  264. return NS_OK;
  265. }
  266. return CallQueryInterface(node, aReturn);
  267. }
  268. NS_IMETHODIMP
  269. TableRowsCollection::ParentDestroyed()
  270. {
  271. // see comment in destructor, do NOT release mParent!
  272. mParent = nullptr;
  273. return NS_OK;
  274. }
  275. /* --------------------------- HTMLTableElement ---------------------------- */
  276. HTMLTableElement::HTMLTableElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
  277. : nsGenericHTMLElement(aNodeInfo),
  278. mTableInheritedAttributes(nullptr)
  279. {
  280. SetHasWeirdParserInsertionMode();
  281. }
  282. HTMLTableElement::~HTMLTableElement()
  283. {
  284. if (mRows) {
  285. mRows->ParentDestroyed();
  286. }
  287. ReleaseInheritedAttributes();
  288. }
  289. JSObject*
  290. HTMLTableElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
  291. {
  292. return HTMLTableElementBinding::Wrap(aCx, this, aGivenProto);
  293. }
  294. NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTableElement)
  295. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTableElement, nsGenericHTMLElement)
  296. NS_IMPL_CYCLE_COLLECTION_UNLINK(mTBodies)
  297. if (tmp->mRows) {
  298. tmp->mRows->ParentDestroyed();
  299. }
  300. NS_IMPL_CYCLE_COLLECTION_UNLINK(mRows)
  301. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  302. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTableElement,
  303. nsGenericHTMLElement)
  304. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTBodies)
  305. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRows)
  306. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  307. NS_IMPL_ADDREF_INHERITED(HTMLTableElement, Element)
  308. NS_IMPL_RELEASE_INHERITED(HTMLTableElement, Element)
  309. // QueryInterface implementation for HTMLTableElement
  310. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLTableElement)
  311. NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
  312. NS_IMPL_ELEMENT_CLONE(HTMLTableElement)
  313. // the DOM spec says border, cellpadding, cellSpacing are all "wstring"
  314. // in fact, they are integers or they are meaningless. so we store them
  315. // here as ints.
  316. nsIHTMLCollection*
  317. HTMLTableElement::Rows()
  318. {
  319. if (!mRows) {
  320. mRows = new TableRowsCollection(this);
  321. }
  322. return mRows;
  323. }
  324. nsIHTMLCollection*
  325. HTMLTableElement::TBodies()
  326. {
  327. if (!mTBodies) {
  328. // Not using NS_GetContentList because this should not be cached
  329. mTBodies = new nsContentList(this,
  330. kNameSpaceID_XHTML,
  331. nsGkAtoms::tbody,
  332. nsGkAtoms::tbody,
  333. false);
  334. }
  335. return mTBodies;
  336. }
  337. already_AddRefed<nsGenericHTMLElement>
  338. HTMLTableElement::CreateTHead()
  339. {
  340. RefPtr<nsGenericHTMLElement> head = GetTHead();
  341. if (!head) {
  342. // Create a new head rowgroup.
  343. RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  344. nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::thead,
  345. getter_AddRefs(nodeInfo));
  346. head = NS_NewHTMLTableSectionElement(nodeInfo.forget());
  347. if (!head) {
  348. return nullptr;
  349. }
  350. ErrorResult rv;
  351. nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild();
  352. nsINode::InsertBefore(*head, refNode, rv);
  353. }
  354. return head.forget();
  355. }
  356. void
  357. HTMLTableElement::DeleteTHead()
  358. {
  359. RefPtr<HTMLTableSectionElement> tHead = GetTHead();
  360. if (tHead) {
  361. mozilla::IgnoredErrorResult rv;
  362. nsINode::RemoveChild(*tHead, rv);
  363. }
  364. }
  365. already_AddRefed<nsGenericHTMLElement>
  366. HTMLTableElement::CreateTFoot()
  367. {
  368. RefPtr<nsGenericHTMLElement> foot = GetTFoot();
  369. if (!foot) {
  370. // create a new foot rowgroup
  371. RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  372. nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tfoot,
  373. getter_AddRefs(nodeInfo));
  374. foot = NS_NewHTMLTableSectionElement(nodeInfo.forget());
  375. if (!foot) {
  376. return nullptr;
  377. }
  378. AppendChildTo(foot, true);
  379. }
  380. return foot.forget();
  381. }
  382. void
  383. HTMLTableElement::DeleteTFoot()
  384. {
  385. RefPtr<HTMLTableSectionElement> tFoot = GetTFoot();
  386. if (tFoot) {
  387. mozilla::IgnoredErrorResult rv;
  388. nsINode::RemoveChild(*tFoot, rv);
  389. }
  390. }
  391. already_AddRefed<nsGenericHTMLElement>
  392. HTMLTableElement::CreateCaption()
  393. {
  394. RefPtr<nsGenericHTMLElement> caption = GetCaption();
  395. if (!caption) {
  396. // Create a new caption.
  397. RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  398. nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::caption,
  399. getter_AddRefs(nodeInfo));
  400. caption = NS_NewHTMLTableCaptionElement(nodeInfo.forget());
  401. if (!caption) {
  402. return nullptr;
  403. }
  404. AppendChildTo(caption, true);
  405. }
  406. return caption.forget();
  407. }
  408. void
  409. HTMLTableElement::DeleteCaption()
  410. {
  411. RefPtr<HTMLTableCaptionElement> caption = GetCaption();
  412. if (caption) {
  413. mozilla::IgnoredErrorResult rv;
  414. nsINode::RemoveChild(*caption, rv);
  415. }
  416. }
  417. already_AddRefed<nsGenericHTMLElement>
  418. HTMLTableElement::CreateTBody()
  419. {
  420. RefPtr<mozilla::dom::NodeInfo> nodeInfo =
  421. OwnerDoc()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::tbody, nullptr,
  422. kNameSpaceID_XHTML,
  423. nsIDOMNode::ELEMENT_NODE);
  424. MOZ_ASSERT(nodeInfo);
  425. RefPtr<nsGenericHTMLElement> newBody =
  426. NS_NewHTMLTableSectionElement(nodeInfo.forget());
  427. MOZ_ASSERT(newBody);
  428. nsCOMPtr<nsIContent> referenceNode = nullptr;
  429. for (nsIContent* child = nsINode::GetLastChild();
  430. child;
  431. child = child->GetPreviousSibling()) {
  432. if (child->IsHTMLElement(nsGkAtoms::tbody)) {
  433. referenceNode = child->GetNextSibling();
  434. break;
  435. }
  436. }
  437. ErrorResult rv;
  438. nsINode::InsertBefore(*newBody, referenceNode, rv);
  439. return newBody.forget();
  440. }
  441. already_AddRefed<nsGenericHTMLElement>
  442. HTMLTableElement::InsertRow(int32_t aIndex, ErrorResult& aError)
  443. {
  444. /* get the ref row at aIndex
  445. if there is one,
  446. get its parent
  447. insert the new row just before the ref row
  448. else
  449. get the first row group
  450. insert the new row as its first child
  451. */
  452. if (aIndex < -1) {
  453. aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  454. return nullptr;
  455. }
  456. nsIHTMLCollection* rows = Rows();
  457. uint32_t rowCount = rows->Length();
  458. if ((uint32_t)aIndex > rowCount && aIndex != -1) {
  459. aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  460. return nullptr;
  461. }
  462. // use local variable refIndex so we can remember original aIndex
  463. uint32_t refIndex = (uint32_t)aIndex;
  464. RefPtr<nsGenericHTMLElement> newRow;
  465. if (rowCount > 0) {
  466. if (refIndex == rowCount || aIndex == -1) {
  467. // we set refIndex to the last row so we can get the last row's
  468. // parent we then do an AppendChild below if (rowCount<aIndex)
  469. refIndex = rowCount - 1;
  470. }
  471. RefPtr<Element> refRow = rows->Item(refIndex);
  472. nsCOMPtr<nsINode> parent = refRow->GetParentNode();
  473. // create the row
  474. RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  475. nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
  476. getter_AddRefs(nodeInfo));
  477. newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
  478. if (newRow) {
  479. // If aIndex is -1 or equal to the number of rows, the new row
  480. // is appended.
  481. if (aIndex == -1 || uint32_t(aIndex) == rowCount) {
  482. parent->AppendChild(*newRow, aError);
  483. } else {
  484. // insert the new row before the reference row we found above
  485. parent->InsertBefore(*newRow, refRow, aError);
  486. }
  487. if (aError.Failed()) {
  488. return nullptr;
  489. }
  490. }
  491. } else {
  492. // the row count was 0, so
  493. // find the last row group and insert there as first child
  494. nsCOMPtr<nsIContent> rowGroup;
  495. for (nsIContent* child = nsINode::GetLastChild();
  496. child;
  497. child = child->GetPreviousSibling()) {
  498. if (child->IsHTMLElement(nsGkAtoms::tbody)) {
  499. rowGroup = child;
  500. break;
  501. }
  502. }
  503. if (!rowGroup) { // need to create a TBODY
  504. RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  505. nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tbody,
  506. getter_AddRefs(nodeInfo));
  507. rowGroup = NS_NewHTMLTableSectionElement(nodeInfo.forget());
  508. if (rowGroup) {
  509. aError = AppendChildTo(rowGroup, true);
  510. if (aError.Failed()) {
  511. return nullptr;
  512. }
  513. }
  514. }
  515. if (rowGroup) {
  516. RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  517. nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
  518. getter_AddRefs(nodeInfo));
  519. newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
  520. if (newRow) {
  521. HTMLTableSectionElement* section =
  522. static_cast<HTMLTableSectionElement*>(rowGroup.get());
  523. nsIHTMLCollection* rows = section->Rows();
  524. nsCOMPtr<nsINode> refNode = rows->Item(0);
  525. rowGroup->InsertBefore(*newRow, refNode, aError);
  526. }
  527. }
  528. }
  529. return newRow.forget();
  530. }
  531. void
  532. HTMLTableElement::DeleteRow(int32_t aIndex, ErrorResult& aError)
  533. {
  534. if (aIndex < -1) {
  535. aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  536. return;
  537. }
  538. nsIHTMLCollection* rows = Rows();
  539. uint32_t refIndex;
  540. if (aIndex == -1) {
  541. refIndex = rows->Length();
  542. if (refIndex == 0) {
  543. return;
  544. }
  545. --refIndex;
  546. } else {
  547. refIndex = (uint32_t)aIndex;
  548. }
  549. nsCOMPtr<nsIContent> row = rows->Item(refIndex);
  550. if (!row) {
  551. aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
  552. return;
  553. }
  554. row->RemoveFromParent();
  555. }
  556. bool
  557. HTMLTableElement::ParseAttribute(int32_t aNamespaceID,
  558. nsIAtom* aAttribute,
  559. const nsAString& aValue,
  560. nsAttrValue& aResult)
  561. {
  562. /* ignore summary, just a string */
  563. if (aNamespaceID == kNameSpaceID_None) {
  564. if (aAttribute == nsGkAtoms::cellspacing ||
  565. aAttribute == nsGkAtoms::cellpadding ||
  566. aAttribute == nsGkAtoms::border) {
  567. return aResult.ParseNonNegativeIntValue(aValue);
  568. }
  569. if (aAttribute == nsGkAtoms::height) {
  570. return aResult.ParseSpecialIntValue(aValue);
  571. }
  572. if (aAttribute == nsGkAtoms::width) {
  573. if (aResult.ParseSpecialIntValue(aValue)) {
  574. // treat 0 width as auto
  575. nsAttrValue::ValueType type = aResult.Type();
  576. return !((type == nsAttrValue::eInteger &&
  577. aResult.GetIntegerValue() == 0) ||
  578. (type == nsAttrValue::ePercent &&
  579. aResult.GetPercentValue() == 0.0f));
  580. }
  581. return false;
  582. }
  583. if (aAttribute == nsGkAtoms::align) {
  584. return ParseTableHAlignValue(aValue, aResult);
  585. }
  586. if (aAttribute == nsGkAtoms::bgcolor ||
  587. aAttribute == nsGkAtoms::bordercolor) {
  588. return aResult.ParseColor(aValue);
  589. }
  590. if (aAttribute == nsGkAtoms::hspace ||
  591. aAttribute == nsGkAtoms::vspace) {
  592. return aResult.ParseIntWithBounds(aValue, 0);
  593. }
  594. }
  595. return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
  596. aAttribute, aValue,
  597. aResult) ||
  598. nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
  599. aResult);
  600. }
  601. void
  602. HTMLTableElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
  603. nsRuleData* aData)
  604. {
  605. // XXX Bug 211636: This function is used by a single style rule
  606. // that's used to match two different type of elements -- tables, and
  607. // table cells. (nsHTMLTableCellElement overrides
  608. // WalkContentStyleRules so that this happens.) This violates the
  609. // nsIStyleRule contract, since it's the same style rule object doing
  610. // the mapping in two different ways. It's also incorrect since it's
  611. // testing the display type of the style context rather than checking
  612. // which *element* it's matching (style rules should not stop matching
  613. // when the display type is changed).
  614. nsPresContext* presContext = aData->mPresContext;
  615. nsCompatibility mode = presContext->CompatibilityMode();
  616. if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TableBorder)) {
  617. // cellspacing
  618. const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellspacing);
  619. nsCSSValue* borderSpacing = aData->ValueForBorderSpacing();
  620. if (value && value->Type() == nsAttrValue::eInteger &&
  621. borderSpacing->GetUnit() == eCSSUnit_Null) {
  622. borderSpacing->
  623. SetFloatValue(float(value->GetIntegerValue()), eCSSUnit_Pixel);
  624. }
  625. }
  626. if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Margin)) {
  627. // align; Check for enumerated type (it may be another type if
  628. // illegal)
  629. const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
  630. if (value && value->Type() == nsAttrValue::eEnum) {
  631. if (value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_CENTER ||
  632. value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) {
  633. nsCSSValue* marginLeft = aData->ValueForMarginLeft();
  634. if (marginLeft->GetUnit() == eCSSUnit_Null)
  635. marginLeft->SetAutoValue();
  636. nsCSSValue* marginRight = aData->ValueForMarginRight();
  637. if (marginRight->GetUnit() == eCSSUnit_Null)
  638. marginRight->SetAutoValue();
  639. }
  640. }
  641. // hspace is mapped into left and right margin,
  642. // vspace is mapped into top and bottom margins
  643. // - *** Quirks Mode only ***
  644. if (eCompatibility_NavQuirks == mode) {
  645. value = aAttributes->GetAttr(nsGkAtoms::hspace);
  646. if (value && value->Type() == nsAttrValue::eInteger) {
  647. nsCSSValue* marginLeft = aData->ValueForMarginLeft();
  648. if (marginLeft->GetUnit() == eCSSUnit_Null)
  649. marginLeft->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
  650. nsCSSValue* marginRight = aData->ValueForMarginRight();
  651. if (marginRight->GetUnit() == eCSSUnit_Null)
  652. marginRight->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
  653. }
  654. value = aAttributes->GetAttr(nsGkAtoms::vspace);
  655. if (value && value->Type() == nsAttrValue::eInteger) {
  656. nsCSSValue* marginTop = aData->ValueForMarginTop();
  657. if (marginTop->GetUnit() == eCSSUnit_Null)
  658. marginTop->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
  659. nsCSSValue* marginBottom = aData->ValueForMarginBottom();
  660. if (marginBottom->GetUnit() == eCSSUnit_Null)
  661. marginBottom->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
  662. }
  663. }
  664. }
  665. if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
  666. // width: value
  667. nsCSSValue* width = aData->ValueForWidth();
  668. if (width->GetUnit() == eCSSUnit_Null) {
  669. const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
  670. if (value && value->Type() == nsAttrValue::eInteger)
  671. width->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
  672. else if (value && value->Type() == nsAttrValue::ePercent)
  673. width->SetPercentValue(value->GetPercentValue());
  674. }
  675. // height: value
  676. nsCSSValue* height = aData->ValueForHeight();
  677. if (height->GetUnit() == eCSSUnit_Null) {
  678. const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
  679. if (value && value->Type() == nsAttrValue::eInteger)
  680. height->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
  681. else if (value && value->Type() == nsAttrValue::ePercent)
  682. height->SetPercentValue(value->GetPercentValue());
  683. }
  684. }
  685. if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Border)) {
  686. // bordercolor
  687. const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bordercolor);
  688. nscolor color;
  689. if (value && presContext->UseDocumentColors() &&
  690. value->GetColorValue(color)) {
  691. nsCSSValue* borderLeftColor = aData->ValueForBorderLeftColor();
  692. if (borderLeftColor->GetUnit() == eCSSUnit_Null)
  693. borderLeftColor->SetColorValue(color);
  694. nsCSSValue* borderRightColor = aData->ValueForBorderRightColor();
  695. if (borderRightColor->GetUnit() == eCSSUnit_Null)
  696. borderRightColor->SetColorValue(color);
  697. nsCSSValue* borderTopColor = aData->ValueForBorderTopColor();
  698. if (borderTopColor->GetUnit() == eCSSUnit_Null)
  699. borderTopColor->SetColorValue(color);
  700. nsCSSValue* borderBottomColor = aData->ValueForBorderBottomColor();
  701. if (borderBottomColor->GetUnit() == eCSSUnit_Null)
  702. borderBottomColor->SetColorValue(color);
  703. }
  704. // border
  705. const nsAttrValue* borderValue = aAttributes->GetAttr(nsGkAtoms::border);
  706. if (borderValue) {
  707. // border = 1 pixel default
  708. int32_t borderThickness = 1;
  709. if (borderValue->Type() == nsAttrValue::eInteger)
  710. borderThickness = borderValue->GetIntegerValue();
  711. // by default, set all border sides to the specified width
  712. nsCSSValue* borderLeftWidth = aData->ValueForBorderLeftWidth();
  713. if (borderLeftWidth->GetUnit() == eCSSUnit_Null)
  714. borderLeftWidth->SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
  715. nsCSSValue* borderRightWidth = aData->ValueForBorderRightWidth();
  716. if (borderRightWidth->GetUnit() == eCSSUnit_Null)
  717. borderRightWidth->SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
  718. nsCSSValue* borderTopWidth = aData->ValueForBorderTopWidth();
  719. if (borderTopWidth->GetUnit() == eCSSUnit_Null)
  720. borderTopWidth->SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
  721. nsCSSValue* borderBottomWidth = aData->ValueForBorderBottomWidth();
  722. if (borderBottomWidth->GetUnit() == eCSSUnit_Null)
  723. borderBottomWidth->SetFloatValue((float)borderThickness, eCSSUnit_Pixel);
  724. }
  725. }
  726. nsGenericHTMLElement::MapBackgroundAttributesInto(aAttributes, aData);
  727. nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
  728. }
  729. NS_IMETHODIMP_(bool)
  730. HTMLTableElement::IsAttributeMapped(const nsIAtom* aAttribute) const
  731. {
  732. static const MappedAttributeEntry attributes[] = {
  733. { &nsGkAtoms::cellpadding },
  734. { &nsGkAtoms::cellspacing },
  735. { &nsGkAtoms::border },
  736. { &nsGkAtoms::width },
  737. { &nsGkAtoms::height },
  738. { &nsGkAtoms::hspace },
  739. { &nsGkAtoms::vspace },
  740. { &nsGkAtoms::bordercolor },
  741. { &nsGkAtoms::align },
  742. { nullptr }
  743. };
  744. static const MappedAttributeEntry* const map[] = {
  745. attributes,
  746. sCommonAttributeMap,
  747. sBackgroundAttributeMap,
  748. };
  749. return FindAttributeDependence(aAttribute, map);
  750. }
  751. nsMapRuleToAttributesFunc
  752. HTMLTableElement::GetAttributeMappingFunction() const
  753. {
  754. return &MapAttributesIntoRule;
  755. }
  756. static void
  757. MapInheritedTableAttributesIntoRule(const nsMappedAttributes* aAttributes,
  758. nsRuleData* aData)
  759. {
  760. if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Padding)) {
  761. const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellpadding);
  762. if (value && value->Type() == nsAttrValue::eInteger) {
  763. // We have cellpadding. This will override our padding values if we
  764. // don't have any set.
  765. nsCSSValue padVal(float(value->GetIntegerValue()), eCSSUnit_Pixel);
  766. nsCSSValue* paddingLeft = aData->ValueForPaddingLeft();
  767. if (paddingLeft->GetUnit() == eCSSUnit_Null) {
  768. *paddingLeft = padVal;
  769. }
  770. nsCSSValue* paddingRight = aData->ValueForPaddingRight();
  771. if (paddingRight->GetUnit() == eCSSUnit_Null) {
  772. *paddingRight = padVal;
  773. }
  774. nsCSSValue* paddingTop = aData->ValueForPaddingTop();
  775. if (paddingTop->GetUnit() == eCSSUnit_Null) {
  776. *paddingTop = padVal;
  777. }
  778. nsCSSValue* paddingBottom = aData->ValueForPaddingBottom();
  779. if (paddingBottom->GetUnit() == eCSSUnit_Null) {
  780. *paddingBottom = padVal;
  781. }
  782. }
  783. }
  784. }
  785. nsMappedAttributes*
  786. HTMLTableElement::GetAttributesMappedForCell()
  787. {
  788. return mTableInheritedAttributes;
  789. }
  790. void
  791. HTMLTableElement::BuildInheritedAttributes()
  792. {
  793. NS_ASSERTION(!mTableInheritedAttributes,
  794. "potential leak, plus waste of work");
  795. MOZ_ASSERT(NS_IsMainThread());
  796. nsIDocument *document = GetComposedDoc();
  797. nsHTMLStyleSheet* sheet = document ?
  798. document->GetAttributeStyleSheet() : nullptr;
  799. RefPtr<nsMappedAttributes> newAttrs;
  800. if (sheet) {
  801. const nsAttrValue* value = mAttrsAndChildren.GetAttr(nsGkAtoms::cellpadding);
  802. if (value) {
  803. RefPtr<nsMappedAttributes> modifiableMapped = new
  804. nsMappedAttributes(sheet, MapInheritedTableAttributesIntoRule);
  805. if (modifiableMapped) {
  806. nsAttrValue val(*value);
  807. bool oldValueSet;
  808. modifiableMapped->SetAndSwapAttr(nsGkAtoms::cellpadding, val,
  809. &oldValueSet);
  810. }
  811. newAttrs = sheet->UniqueMappedAttributes(modifiableMapped);
  812. NS_ASSERTION(newAttrs, "out of memory, but handling gracefully");
  813. if (newAttrs != modifiableMapped) {
  814. // Reset the stylesheet of modifiableMapped so that it doesn't
  815. // spend time trying to remove itself from the hash. There is no
  816. // risk that modifiableMapped is in the hash since we created
  817. // it ourselves and it didn't come from the stylesheet (in which
  818. // case it would not have been modifiable).
  819. modifiableMapped->DropStyleSheetReference();
  820. }
  821. }
  822. mTableInheritedAttributes = newAttrs;
  823. NS_IF_ADDREF(mTableInheritedAttributes);
  824. }
  825. }
  826. void
  827. HTMLTableElement::ReleaseInheritedAttributes()
  828. {
  829. NS_IF_RELEASE(mTableInheritedAttributes);
  830. }
  831. nsresult
  832. HTMLTableElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
  833. nsIContent* aBindingParent,
  834. bool aCompileEventHandlers)
  835. {
  836. ReleaseInheritedAttributes();
  837. nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
  838. aBindingParent,
  839. aCompileEventHandlers);
  840. NS_ENSURE_SUCCESS(rv, rv);
  841. BuildInheritedAttributes();
  842. return NS_OK;
  843. }
  844. void
  845. HTMLTableElement::UnbindFromTree(bool aDeep, bool aNullParent)
  846. {
  847. ReleaseInheritedAttributes();
  848. nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
  849. }
  850. nsresult
  851. HTMLTableElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  852. const nsAttrValueOrString* aValue,
  853. bool aNotify)
  854. {
  855. if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) {
  856. ReleaseInheritedAttributes();
  857. }
  858. return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
  859. aNotify);
  860. }
  861. nsresult
  862. HTMLTableElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
  863. const nsAttrValue* aValue,
  864. const nsAttrValue* aOldValue, bool aNotify)
  865. {
  866. if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) {
  867. BuildInheritedAttributes();
  868. }
  869. return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
  870. aOldValue, aNotify);
  871. }
  872. } // namespace dom
  873. } // namespace mozilla