1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Aaron Leventhal <aaronl@netscape.com> (original author)
24 : * Alexander Surkov <surkov.alexander@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsHTMLTableAccessible.h"
41 :
42 : #include "nsAccessibilityService.h"
43 : #include "nsAccTreeWalker.h"
44 : #include "nsAccUtils.h"
45 : #include "nsDocAccessible.h"
46 : #include "nsTextEquivUtils.h"
47 : #include "Relation.h"
48 : #include "Role.h"
49 : #include "States.h"
50 :
51 : #include "nsIAccessibleRelation.h"
52 : #include "nsIDOMElement.h"
53 : #include "nsIDOMDocument.h"
54 : #include "nsIDOMRange.h"
55 : #include "nsISelectionPrivate.h"
56 : #include "nsINameSpaceManager.h"
57 : #include "nsIDOMNodeList.h"
58 : #include "nsIDOMHTMLCollection.h"
59 : #include "nsIDOMHTMLTableCellElement.h"
60 : #include "nsIDOMHTMLTableElement.h"
61 : #include "nsIDOMHTMLTableRowElement.h"
62 : #include "nsIDOMHTMLTableSectionElem.h"
63 : #include "nsIDocument.h"
64 : #include "nsIPresShell.h"
65 : #include "nsITableLayout.h"
66 : #include "nsITableCellLayout.h"
67 : #include "nsFrameSelection.h"
68 : #include "nsLayoutErrors.h"
69 : #include "nsArrayUtils.h"
70 : #include "nsComponentManagerUtils.h"
71 :
72 : using namespace mozilla::a11y;
73 :
74 : ////////////////////////////////////////////////////////////////////////////////
75 : // nsHTMLTableCellAccessible
76 : ////////////////////////////////////////////////////////////////////////////////
77 :
78 0 : nsHTMLTableCellAccessible::
79 : nsHTMLTableCellAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
80 0 : nsHyperTextAccessibleWrap(aContent, aDoc)
81 : {
82 0 : }
83 :
84 : ////////////////////////////////////////////////////////////////////////////////
85 : // nsHTMLTableCellAccessible: nsISupports implementation
86 :
87 0 : NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLTableCellAccessible,
88 : nsHyperTextAccessible,
89 : nsIAccessibleTableCell)
90 :
91 : ////////////////////////////////////////////////////////////////////////////////
92 : // nsHTMLTableCellAccessible: nsAccessible implementation
93 :
94 : role
95 0 : nsHTMLTableCellAccessible::NativeRole()
96 : {
97 0 : return roles::CELL;
98 : }
99 :
100 : PRUint64
101 0 : nsHTMLTableCellAccessible::NativeState()
102 : {
103 0 : PRUint64 state = nsHyperTextAccessibleWrap::NativeState();
104 :
105 0 : nsIFrame *frame = mContent->GetPrimaryFrame();
106 0 : NS_ASSERTION(frame, "No frame for valid cell accessible!");
107 :
108 0 : if (frame) {
109 0 : state |= states::SELECTABLE;
110 0 : if (frame->IsSelected())
111 0 : state |= states::SELECTED;
112 : }
113 :
114 0 : return state;
115 : }
116 :
117 : nsresult
118 0 : nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
119 : {
120 0 : if (IsDefunct())
121 0 : return NS_ERROR_FAILURE;
122 :
123 0 : nsresult rv = nsHyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
124 0 : NS_ENSURE_SUCCESS(rv, rv);
125 :
126 : // table-cell-index attribute
127 0 : nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
128 0 : if (!tableAcc)
129 0 : return NS_OK;
130 :
131 0 : PRInt32 rowIdx = -1, colIdx = -1;
132 0 : rv = GetCellIndexes(rowIdx, colIdx);
133 0 : NS_ENSURE_SUCCESS(rv, rv);
134 :
135 0 : PRInt32 idx = -1;
136 0 : rv = tableAcc->GetCellIndexAt(rowIdx, colIdx, &idx);
137 0 : NS_ENSURE_SUCCESS(rv, rv);
138 :
139 0 : nsAutoString stringIdx;
140 0 : stringIdx.AppendInt(idx);
141 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::tableCellIndex, stringIdx);
142 :
143 : // abbr attribute
144 :
145 : // Pick up object attribute from abbr DOM element (a child of the cell) or
146 : // from abbr DOM attribute.
147 0 : nsAutoString abbrText;
148 0 : if (GetChildCount() == 1) {
149 0 : nsAccessible* abbr = FirstChild();
150 0 : if (abbr->IsAbbreviation()) {
151 : nsTextEquivUtils::
152 0 : AppendTextEquivFromTextContent(abbr->GetContent()->GetFirstChild(),
153 0 : &abbrText);
154 : }
155 : }
156 0 : if (abbrText.IsEmpty())
157 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText);
158 :
159 0 : if (!abbrText.IsEmpty())
160 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::abbr, abbrText);
161 :
162 : // axis attribute
163 0 : nsAutoString axisText;
164 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText);
165 0 : if (!axisText.IsEmpty())
166 0 : nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::axis, axisText);
167 :
168 0 : return NS_OK;
169 : }
170 :
171 : ////////////////////////////////////////////////////////////////////////////////
172 : // nsHTMLTableCellAccessible: nsIAccessibleTableCell implementation
173 :
174 : NS_IMETHODIMP
175 0 : nsHTMLTableCellAccessible::GetTable(nsIAccessibleTable **aTable)
176 : {
177 0 : NS_ENSURE_ARG_POINTER(aTable);
178 0 : *aTable = nsnull;
179 :
180 0 : if (IsDefunct())
181 0 : return NS_OK;
182 :
183 0 : nsCOMPtr<nsIAccessibleTable> table = GetTableAccessible();
184 0 : table.swap(*aTable);
185 :
186 0 : return NS_OK;
187 : }
188 :
189 : NS_IMETHODIMP
190 0 : nsHTMLTableCellAccessible::GetColumnIndex(PRInt32 *aColumnIndex)
191 : {
192 0 : NS_ENSURE_ARG_POINTER(aColumnIndex);
193 0 : *aColumnIndex = -1;
194 :
195 0 : if (IsDefunct())
196 0 : return NS_ERROR_FAILURE;
197 :
198 0 : nsITableCellLayout* cellLayout = GetCellLayout();
199 0 : NS_ENSURE_STATE(cellLayout);
200 :
201 0 : return cellLayout->GetColIndex(*aColumnIndex);
202 : }
203 :
204 : NS_IMETHODIMP
205 0 : nsHTMLTableCellAccessible::GetRowIndex(PRInt32 *aRowIndex)
206 : {
207 0 : NS_ENSURE_ARG_POINTER(aRowIndex);
208 0 : *aRowIndex = -1;
209 :
210 0 : if (IsDefunct())
211 0 : return NS_ERROR_FAILURE;
212 :
213 0 : nsITableCellLayout* cellLayout = GetCellLayout();
214 0 : NS_ENSURE_STATE(cellLayout);
215 :
216 0 : return cellLayout->GetRowIndex(*aRowIndex);
217 : }
218 :
219 : NS_IMETHODIMP
220 0 : nsHTMLTableCellAccessible::GetColumnExtent(PRInt32 *aExtentCount)
221 : {
222 0 : NS_ENSURE_ARG_POINTER(aExtentCount);
223 0 : *aExtentCount = 1;
224 :
225 0 : PRInt32 rowIdx = -1, colIdx = -1;
226 0 : GetCellIndexes(rowIdx, colIdx);
227 :
228 0 : nsCOMPtr<nsIAccessibleTable> table = GetTableAccessible();
229 0 : NS_ENSURE_STATE(table);
230 :
231 0 : return table->GetColumnExtentAt(rowIdx, colIdx, aExtentCount);
232 : }
233 :
234 : NS_IMETHODIMP
235 0 : nsHTMLTableCellAccessible::GetRowExtent(PRInt32 *aExtentCount)
236 : {
237 0 : NS_ENSURE_ARG_POINTER(aExtentCount);
238 0 : *aExtentCount = 1;
239 :
240 0 : PRInt32 rowIdx = -1, colIdx = -1;
241 0 : GetCellIndexes(rowIdx, colIdx);
242 :
243 0 : nsCOMPtr<nsIAccessibleTable> table = GetTableAccessible();
244 0 : NS_ENSURE_STATE(table);
245 :
246 0 : return table->GetRowExtentAt(rowIdx, colIdx, aExtentCount);
247 : }
248 :
249 : NS_IMETHODIMP
250 0 : nsHTMLTableCellAccessible::GetColumnHeaderCells(nsIArray **aHeaderCells)
251 : {
252 0 : NS_ENSURE_ARG_POINTER(aHeaderCells);
253 0 : *aHeaderCells = nsnull;
254 :
255 0 : if (IsDefunct())
256 0 : return NS_ERROR_FAILURE;
257 :
258 0 : return GetHeaderCells(nsAccUtils::eColumnHeaderCells, aHeaderCells);
259 : }
260 :
261 : NS_IMETHODIMP
262 0 : nsHTMLTableCellAccessible::GetRowHeaderCells(nsIArray **aHeaderCells)
263 : {
264 0 : NS_ENSURE_ARG_POINTER(aHeaderCells);
265 0 : *aHeaderCells = nsnull;
266 :
267 0 : if (IsDefunct())
268 0 : return NS_ERROR_FAILURE;
269 :
270 0 : return GetHeaderCells(nsAccUtils::eRowHeaderCells, aHeaderCells);
271 : }
272 :
273 : NS_IMETHODIMP
274 0 : nsHTMLTableCellAccessible::IsSelected(bool *aIsSelected)
275 : {
276 0 : NS_ENSURE_ARG_POINTER(aIsSelected);
277 0 : *aIsSelected = false;
278 :
279 0 : if (IsDefunct())
280 0 : return NS_ERROR_FAILURE;
281 :
282 0 : PRInt32 rowIdx = -1, colIdx = -1;
283 0 : GetCellIndexes(rowIdx, colIdx);
284 :
285 0 : nsCOMPtr<nsIAccessibleTable> table = GetTableAccessible();
286 0 : NS_ENSURE_STATE(table);
287 :
288 0 : return table->IsCellSelected(rowIdx, colIdx, aIsSelected);
289 : }
290 :
291 : ////////////////////////////////////////////////////////////////////////////////
292 : // nsHTMLTableCellAccessible: protected implementation
293 :
294 : already_AddRefed<nsIAccessibleTable>
295 0 : nsHTMLTableCellAccessible::GetTableAccessible()
296 : {
297 0 : nsAccessible* parent = this;
298 0 : while ((parent = parent->Parent())) {
299 0 : roles::Role role = parent->Role();
300 0 : if (role == roles::TABLE || role == roles::TREE_TABLE) {
301 0 : nsIAccessibleTable* tableAcc = nsnull;
302 0 : CallQueryInterface(parent, &tableAcc);
303 0 : return tableAcc;
304 : }
305 : }
306 :
307 0 : return nsnull;
308 : }
309 :
310 : nsITableCellLayout*
311 0 : nsHTMLTableCellAccessible::GetCellLayout()
312 : {
313 0 : nsIFrame *frame = mContent->GetPrimaryFrame();
314 0 : NS_ASSERTION(frame, "The frame cannot be obtaied for HTML table cell.");
315 0 : if (!frame)
316 0 : return nsnull;
317 :
318 0 : nsITableCellLayout *cellLayout = do_QueryFrame(frame);
319 0 : return cellLayout;
320 : }
321 :
322 : nsresult
323 0 : nsHTMLTableCellAccessible::GetCellIndexes(PRInt32& aRowIndex,
324 : PRInt32& aColIndex)
325 : {
326 0 : nsITableCellLayout *cellLayout = GetCellLayout();
327 0 : NS_ENSURE_STATE(cellLayout);
328 :
329 0 : return cellLayout->GetCellIndexes(aRowIndex, aColIndex);
330 : }
331 :
332 : nsresult
333 0 : nsHTMLTableCellAccessible::GetHeaderCells(PRInt32 aRowOrColumnHeaderCell,
334 : nsIArray **aHeaderCells)
335 : {
336 : // Get header cells from @header attribute.
337 0 : IDRefsIterator iter(mContent, nsGkAtoms::headers);
338 0 : nsIContent* headerCellElm = iter.NextElem();
339 0 : if (headerCellElm) {
340 0 : nsresult rv = NS_OK;
341 : nsCOMPtr<nsIMutableArray> headerCells =
342 0 : do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
343 0 : NS_ENSURE_SUCCESS(rv, rv);
344 0 : roles::Role desiredRole = static_cast<roles::Role>(-1) ;
345 0 : if (aRowOrColumnHeaderCell == nsAccUtils::eRowHeaderCells)
346 0 : desiredRole = roles::ROWHEADER;
347 0 : else if (aRowOrColumnHeaderCell == nsAccUtils::eColumnHeaderCells)
348 0 : desiredRole = roles::COLUMNHEADER;
349 :
350 0 : do {
351 0 : nsAccessible* headerCell = mDoc->GetAccessible(headerCellElm);
352 :
353 0 : if (headerCell && headerCell->Role() == desiredRole)
354 0 : headerCells->AppendElement(static_cast<nsIAccessible*>(headerCell),
355 0 : false);
356 : } while ((headerCellElm = iter.NextElem()));
357 :
358 0 : NS_ADDREF(*aHeaderCells = headerCells);
359 0 : return NS_OK;
360 : }
361 :
362 : // Otherwise calculate header cells from hierarchy (see 11.4.3 "Algorithm to
363 : // find heading information" of w3c HTML 4.01).
364 0 : nsCOMPtr<nsIAccessibleTable> table = GetTableAccessible();
365 0 : if (table) {
366 : return nsAccUtils::GetHeaderCellsFor(table, this, aRowOrColumnHeaderCell,
367 0 : aHeaderCells);
368 : }
369 :
370 0 : return NS_OK;
371 : }
372 :
373 : ////////////////////////////////////////////////////////////////////////////////
374 : // nsHTMLTableHeaderAccessible
375 : ////////////////////////////////////////////////////////////////////////////////
376 :
377 0 : nsHTMLTableHeaderCellAccessible::
378 : nsHTMLTableHeaderCellAccessible(nsIContent* aContent,
379 : nsDocAccessible* aDoc) :
380 0 : nsHTMLTableCellAccessible(aContent, aDoc)
381 : {
382 0 : }
383 :
384 : ////////////////////////////////////////////////////////////////////////////////
385 : // nsHTMLTableHeaderAccessible: nsAccessible implementation
386 :
387 : role
388 0 : nsHTMLTableHeaderCellAccessible::NativeRole()
389 : {
390 : // Check value of @scope attribute.
391 : static nsIContent::AttrValuesArray scopeValues[] =
392 : {&nsGkAtoms::col, &nsGkAtoms::row, nsnull};
393 : PRInt32 valueIdx =
394 0 : mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope,
395 0 : scopeValues, eCaseMatters);
396 :
397 0 : switch (valueIdx) {
398 : case 0:
399 0 : return roles::COLUMNHEADER;
400 : case 1:
401 0 : return roles::ROWHEADER;
402 : }
403 :
404 : // Assume it's columnheader if there are headers in siblings, oterwise
405 : // rowheader.
406 0 : nsIContent* parentContent = mContent->GetParent();
407 0 : if (!parentContent) {
408 0 : NS_ERROR("Deattached content on alive accessible?");
409 0 : return roles::NOTHING;
410 : }
411 :
412 0 : for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent;
413 0 : siblingContent = siblingContent->GetPreviousSibling()) {
414 0 : if (siblingContent->IsElement()) {
415 0 : return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
416 0 : roles::COLUMNHEADER : roles::ROWHEADER;
417 : }
418 : }
419 :
420 0 : for (nsIContent* siblingContent = mContent->GetNextSibling(); siblingContent;
421 0 : siblingContent = siblingContent->GetNextSibling()) {
422 0 : if (siblingContent->IsElement()) {
423 0 : return nsCoreUtils::IsHTMLTableHeader(siblingContent) ?
424 0 : roles::COLUMNHEADER : roles::ROWHEADER;
425 : }
426 : }
427 :
428 : // No elements in siblings what means the table has one column only. Therefore
429 : // it should be column header.
430 0 : return roles::COLUMNHEADER;
431 : }
432 :
433 : ////////////////////////////////////////////////////////////////////////////////
434 : // nsHTMLTableAccessible
435 : ////////////////////////////////////////////////////////////////////////////////
436 :
437 0 : nsHTMLTableAccessible::
438 : nsHTMLTableAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
439 0 : nsAccessibleWrap(aContent, aDoc)
440 : {
441 0 : }
442 :
443 : ////////////////////////////////////////////////////////////////////////////////
444 : // nsHTMLTableAccessible: nsISupports implementation
445 :
446 0 : NS_IMPL_ISUPPORTS_INHERITED2(nsHTMLTableAccessible, nsAccessible,
447 : nsHTMLTableAccessible, nsIAccessibleTable)
448 :
449 :
450 : ////////////////////////////////////////////////////////////////////////////////
451 : // nsHTMLTableAccessible: nsAccessible implementation
452 :
453 : void
454 0 : nsHTMLTableAccessible::CacheChildren()
455 : {
456 : // Move caption accessible so that it's the first child. Check for the first
457 : // caption only, because nsAccessibilityService ensures we don't create
458 : // accessibles for the other captions, since only the first is actually
459 : // visible.
460 0 : nsAccTreeWalker walker(mDoc, mContent, CanHaveAnonChildren());
461 :
462 0 : nsAccessible* child = nsnull;
463 0 : while ((child = walker.NextChild())) {
464 0 : if (child->Role() == roles::CAPTION) {
465 0 : InsertChildAt(0, child);
466 0 : while ((child = walker.NextChild()) && AppendChild(child));
467 0 : break;
468 : }
469 0 : AppendChild(child);
470 : }
471 0 : }
472 :
473 : role
474 0 : nsHTMLTableAccessible::NativeRole()
475 : {
476 0 : return roles::TABLE;
477 : }
478 :
479 : PRUint64
480 0 : nsHTMLTableAccessible::NativeState()
481 : {
482 0 : return nsAccessible::NativeState() | states::READONLY;
483 : }
484 :
485 : nsresult
486 0 : nsHTMLTableAccessible::GetNameInternal(nsAString& aName)
487 : {
488 0 : nsAccessible::GetNameInternal(aName);
489 0 : if (!aName.IsEmpty())
490 0 : return NS_OK;
491 :
492 : // Use table caption as a name.
493 0 : nsAccessible* caption = Caption();
494 0 : if (caption) {
495 0 : nsIContent* captionContent = caption->GetContent();
496 0 : if (captionContent) {
497 0 : nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
498 0 : if (!aName.IsEmpty())
499 0 : return NS_OK;
500 : }
501 : }
502 :
503 : // If no caption then use summary as a name.
504 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName);
505 0 : return NS_OK;
506 : }
507 :
508 : nsresult
509 0 : nsHTMLTableAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
510 : {
511 0 : nsresult rv = nsAccessibleWrap::GetAttributesInternal(aAttributes);
512 0 : NS_ENSURE_SUCCESS(rv, rv);
513 :
514 : bool isProbablyForLayout;
515 0 : IsProbablyForLayout(&isProbablyForLayout);
516 0 : if (isProbablyForLayout) {
517 0 : nsAutoString oldValueUnused;
518 0 : aAttributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"),
519 0 : NS_LITERAL_STRING("true"), oldValueUnused);
520 : }
521 :
522 0 : return NS_OK;
523 : }
524 :
525 : ////////////////////////////////////////////////////////////////////////////////
526 : // nsHTMLTableAccessible: nsIAccessible implementation
527 :
528 : Relation
529 0 : nsHTMLTableAccessible::RelationByType(PRUint32 aType)
530 : {
531 0 : Relation rel = nsAccessibleWrap::RelationByType(aType);
532 0 : if (aType == nsIAccessibleRelation::RELATION_LABELLED_BY)
533 0 : rel.AppendTarget(Caption());
534 :
535 : return rel;
536 : }
537 :
538 : ////////////////////////////////////////////////////////////////////////////////
539 : // nsHTMLTableAccessible: nsIAccessibleTable implementation
540 :
541 : NS_IMETHODIMP
542 0 : nsHTMLTableAccessible::GetCaption(nsIAccessible **aCaption)
543 : {
544 0 : NS_ENSURE_ARG_POINTER(aCaption);
545 :
546 0 : NS_IF_ADDREF(*aCaption = Caption());
547 0 : return NS_OK;
548 : }
549 :
550 : NS_IMETHODIMP
551 0 : nsHTMLTableAccessible::GetSummary(nsAString &aSummary)
552 : {
553 0 : nsCOMPtr<nsIDOMHTMLTableElement> table(do_QueryInterface(mContent));
554 0 : NS_ENSURE_TRUE(table, NS_ERROR_FAILURE);
555 :
556 0 : return table->GetSummary(aSummary);
557 : }
558 :
559 : NS_IMETHODIMP
560 0 : nsHTMLTableAccessible::GetColumnCount(PRInt32 *acolumnCount)
561 : {
562 0 : NS_ENSURE_ARG_POINTER(acolumnCount);
563 0 : *acolumnCount = nsnull;
564 :
565 0 : if (IsDefunct())
566 0 : return NS_ERROR_FAILURE;
567 :
568 0 : nsITableLayout *tableLayout = GetTableLayout();
569 0 : NS_ENSURE_STATE(tableLayout);
570 :
571 : PRInt32 rows;
572 0 : return tableLayout->GetTableSize(rows, *acolumnCount);
573 : }
574 :
575 : NS_IMETHODIMP
576 0 : nsHTMLTableAccessible::GetRowCount(PRInt32 *arowCount)
577 : {
578 0 : NS_ENSURE_ARG_POINTER(arowCount);
579 0 : *arowCount = 0;
580 :
581 0 : if (IsDefunct())
582 0 : return NS_ERROR_FAILURE;
583 :
584 0 : nsITableLayout *tableLayout = GetTableLayout();
585 0 : NS_ENSURE_STATE(tableLayout);
586 :
587 : PRInt32 columns;
588 0 : return tableLayout->GetTableSize(*arowCount, columns);
589 : }
590 :
591 : NS_IMETHODIMP
592 0 : nsHTMLTableAccessible::GetSelectedCellCount(PRUint32* aCount)
593 : {
594 0 : NS_ENSURE_ARG_POINTER(aCount);
595 0 : *aCount = 0;
596 :
597 0 : PRInt32 rowCount = 0;
598 0 : nsresult rv = GetRowCount(&rowCount);
599 0 : NS_ENSURE_SUCCESS(rv, rv);
600 :
601 0 : PRInt32 columnCount = 0;
602 0 : rv = GetColumnCount(&columnCount);
603 0 : NS_ENSURE_SUCCESS(rv, rv);
604 :
605 0 : nsITableLayout *tableLayout = GetTableLayout();
606 0 : NS_ENSURE_STATE(tableLayout);
607 :
608 0 : nsCOMPtr<nsIDOMElement> domElement;
609 0 : PRInt32 startRowIndex = 0, startColIndex = 0,
610 : rowSpan, colSpan, actualRowSpan, actualColSpan;
611 0 : bool isSelected = false;
612 :
613 : PRInt32 rowIndex;
614 0 : for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
615 : PRInt32 columnIndex;
616 0 : for (columnIndex = 0; columnIndex < columnCount; columnIndex++) {
617 : rv = tableLayout->GetCellDataAt(rowIndex, columnIndex,
618 0 : *getter_AddRefs(domElement),
619 : startRowIndex, startColIndex,
620 : rowSpan, colSpan,
621 : actualRowSpan, actualColSpan,
622 0 : isSelected);
623 :
624 0 : if (NS_SUCCEEDED(rv) && startRowIndex == rowIndex &&
625 : startColIndex == columnIndex && isSelected) {
626 0 : (*aCount)++;
627 : }
628 : }
629 : }
630 :
631 0 : return NS_OK;
632 : }
633 :
634 : NS_IMETHODIMP
635 0 : nsHTMLTableAccessible::GetSelectedColumnCount(PRUint32* aCount)
636 : {
637 0 : NS_ENSURE_ARG_POINTER(aCount);
638 0 : *aCount = 0;
639 :
640 0 : PRInt32 count = 0;
641 0 : nsresult rv = GetColumnCount(&count);
642 0 : NS_ENSURE_SUCCESS(rv, rv);
643 :
644 : PRInt32 index;
645 0 : for (index = 0; index < count; index++) {
646 0 : bool state = false;
647 0 : rv = IsColumnSelected(index, &state);
648 0 : NS_ENSURE_SUCCESS(rv, rv);
649 :
650 0 : if (state)
651 0 : (*aCount)++;
652 : }
653 :
654 0 : return NS_OK;
655 : }
656 :
657 : NS_IMETHODIMP
658 0 : nsHTMLTableAccessible::GetSelectedRowCount(PRUint32* aCount)
659 : {
660 0 : NS_ENSURE_ARG_POINTER(aCount);
661 0 : *aCount = 0;
662 :
663 0 : PRInt32 count = 0;
664 0 : nsresult rv = GetRowCount(&count);
665 0 : NS_ENSURE_SUCCESS(rv, rv);
666 :
667 : PRInt32 index;
668 0 : for (index = 0; index < count; index++) {
669 0 : bool state = false;
670 0 : rv = IsRowSelected(index, &state);
671 0 : NS_ENSURE_SUCCESS(rv, rv);
672 :
673 0 : if (state)
674 0 : (*aCount)++;
675 : }
676 :
677 0 : return NS_OK;
678 : }
679 :
680 : NS_IMETHODIMP
681 0 : nsHTMLTableAccessible::GetSelectedCells(nsIArray **aCells)
682 : {
683 0 : NS_ENSURE_ARG_POINTER(aCells);
684 0 : *aCells = nsnull;
685 :
686 0 : PRInt32 rowCount = 0;
687 0 : nsresult rv = GetRowCount(&rowCount);
688 0 : NS_ENSURE_SUCCESS(rv, rv);
689 :
690 0 : PRInt32 columnCount = 0;
691 0 : rv = GetColumnCount(&columnCount);
692 0 : NS_ENSURE_SUCCESS(rv, rv);
693 :
694 0 : nsITableLayout *tableLayout = GetTableLayout();
695 0 : NS_ENSURE_STATE(tableLayout);
696 :
697 : nsCOMPtr<nsIMutableArray> selCells =
698 0 : do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
699 0 : NS_ENSURE_SUCCESS(rv, rv);
700 :
701 0 : nsCOMPtr<nsIDOMElement> cellElement;
702 0 : PRInt32 startRowIndex = 0, startColIndex = 0,
703 : rowSpan, colSpan, actualRowSpan, actualColSpan;
704 0 : bool isSelected = false;
705 :
706 : PRInt32 rowIndex, index;
707 0 : for (rowIndex = 0, index = 0; rowIndex < rowCount; rowIndex++) {
708 : PRInt32 columnIndex;
709 0 : for (columnIndex = 0; columnIndex < columnCount; columnIndex++, index++) {
710 : rv = tableLayout->GetCellDataAt(rowIndex, columnIndex,
711 0 : *getter_AddRefs(cellElement),
712 : startRowIndex, startColIndex,
713 : rowSpan, colSpan,
714 : actualRowSpan, actualColSpan,
715 0 : isSelected);
716 :
717 0 : if (NS_SUCCEEDED(rv) && startRowIndex == rowIndex &&
718 : startColIndex == columnIndex && isSelected) {
719 0 : nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
720 0 : nsAccessible *cell = mDoc->GetAccessible(cellContent);
721 0 : selCells->AppendElement(static_cast<nsIAccessible*>(cell), false);
722 : }
723 : }
724 : }
725 :
726 0 : NS_ADDREF(*aCells = selCells);
727 0 : return NS_OK;
728 : }
729 :
730 : NS_IMETHODIMP
731 0 : nsHTMLTableAccessible::GetSelectedCellIndices(PRUint32 *aNumCells,
732 : PRInt32 **aCells)
733 : {
734 0 : NS_ENSURE_ARG_POINTER(aNumCells);
735 0 : *aNumCells = 0;
736 0 : NS_ENSURE_ARG_POINTER(aCells);
737 0 : *aCells = nsnull;
738 :
739 0 : PRInt32 rowCount = 0;
740 0 : nsresult rv = GetRowCount(&rowCount);
741 0 : NS_ENSURE_SUCCESS(rv, rv);
742 :
743 0 : PRInt32 columnCount = 0;
744 0 : rv = GetColumnCount(&columnCount);
745 0 : NS_ENSURE_SUCCESS(rv, rv);
746 :
747 0 : nsITableLayout *tableLayout = GetTableLayout();
748 0 : NS_ENSURE_STATE(tableLayout);
749 :
750 0 : nsCOMPtr<nsIDOMElement> domElement;
751 0 : PRInt32 startRowIndex = 0, startColIndex = 0,
752 : rowSpan, colSpan, actualRowSpan, actualColSpan;
753 0 : bool isSelected = false;
754 :
755 0 : PRInt32 cellsCount = columnCount * rowCount;
756 0 : nsAutoArrayPtr<bool> states(new bool[cellsCount]);
757 0 : NS_ENSURE_TRUE(states, NS_ERROR_OUT_OF_MEMORY);
758 :
759 : PRInt32 rowIndex, index;
760 0 : for (rowIndex = 0, index = 0; rowIndex < rowCount; rowIndex++) {
761 : PRInt32 columnIndex;
762 0 : for (columnIndex = 0; columnIndex < columnCount; columnIndex++, index++) {
763 : rv = tableLayout->GetCellDataAt(rowIndex, columnIndex,
764 0 : *getter_AddRefs(domElement),
765 : startRowIndex, startColIndex,
766 : rowSpan, colSpan,
767 : actualRowSpan, actualColSpan,
768 0 : isSelected);
769 :
770 0 : if (NS_SUCCEEDED(rv) && startRowIndex == rowIndex &&
771 : startColIndex == columnIndex && isSelected) {
772 0 : states[index] = true;
773 0 : (*aNumCells)++;
774 : } else {
775 0 : states[index] = false;
776 : }
777 : }
778 : }
779 :
780 : PRInt32 *cellsArray =
781 0 : static_cast<PRInt32*>(nsMemory::Alloc((*aNumCells) * sizeof(PRInt32)));
782 0 : NS_ENSURE_TRUE(cellsArray, NS_ERROR_OUT_OF_MEMORY);
783 :
784 0 : PRInt32 curr = 0;
785 0 : for (rowIndex = 0, index = 0; rowIndex < rowCount; rowIndex++) {
786 : PRInt32 columnIndex;
787 0 : for (columnIndex = 0; columnIndex < columnCount; columnIndex++, index++) {
788 0 : if (states[index]) {
789 0 : PRInt32 cellIndex = -1;
790 0 : GetCellIndexAt(rowIndex, columnIndex, &cellIndex);
791 0 : cellsArray[curr++] = cellIndex;
792 : }
793 : }
794 : }
795 :
796 0 : *aCells = cellsArray;
797 0 : return NS_OK;
798 : }
799 :
800 : NS_IMETHODIMP
801 0 : nsHTMLTableAccessible::GetSelectedColumnIndices(PRUint32 *aNumColumns,
802 : PRInt32 **aColumns)
803 : {
804 0 : nsresult rv = NS_OK;
805 :
806 : PRInt32 columnCount;
807 0 : rv = GetColumnCount(&columnCount);
808 0 : NS_ENSURE_SUCCESS(rv, rv);
809 :
810 0 : bool *states = new bool[columnCount];
811 0 : NS_ENSURE_TRUE(states, NS_ERROR_OUT_OF_MEMORY);
812 :
813 0 : *aNumColumns = 0;
814 : PRInt32 index;
815 0 : for (index = 0; index < columnCount; index++) {
816 0 : rv = IsColumnSelected(index, &states[index]);
817 0 : NS_ENSURE_SUCCESS(rv, rv);
818 :
819 0 : if (states[index]) {
820 0 : (*aNumColumns)++;
821 : }
822 : }
823 :
824 0 : PRInt32 *outArray = (PRInt32 *)nsMemory::Alloc((*aNumColumns) * sizeof(PRInt32));
825 0 : if (!outArray) {
826 0 : delete []states;
827 0 : return NS_ERROR_OUT_OF_MEMORY;
828 : }
829 :
830 0 : PRInt32 curr = 0;
831 0 : for (index = 0; index < columnCount; index++) {
832 0 : if (states[index]) {
833 0 : outArray[curr++] = index;
834 : }
835 : }
836 :
837 0 : delete []states;
838 0 : *aColumns = outArray;
839 0 : return rv;
840 : }
841 :
842 : NS_IMETHODIMP
843 0 : nsHTMLTableAccessible::GetSelectedRowIndices(PRUint32 *aNumRows,
844 : PRInt32 **aRows)
845 : {
846 0 : nsresult rv = NS_OK;
847 :
848 : PRInt32 rowCount;
849 0 : rv = GetRowCount(&rowCount);
850 0 : NS_ENSURE_SUCCESS(rv, rv);
851 :
852 0 : bool *states = new bool[rowCount];
853 0 : NS_ENSURE_TRUE(states, NS_ERROR_OUT_OF_MEMORY);
854 :
855 0 : *aNumRows = 0;
856 : PRInt32 index;
857 0 : for (index = 0; index < rowCount; index++) {
858 0 : rv = IsRowSelected(index, &states[index]);
859 0 : NS_ENSURE_SUCCESS(rv, rv);
860 :
861 0 : if (states[index]) {
862 0 : (*aNumRows)++;
863 : }
864 : }
865 :
866 0 : PRInt32 *outArray = (PRInt32 *)nsMemory::Alloc((*aNumRows) * sizeof(PRInt32));
867 0 : if (!outArray) {
868 0 : delete []states;
869 0 : return NS_ERROR_OUT_OF_MEMORY;
870 : }
871 :
872 0 : PRInt32 curr = 0;
873 0 : for (index = 0; index < rowCount; index++) {
874 0 : if (states[index]) {
875 0 : outArray[curr++] = index;
876 : }
877 : }
878 :
879 0 : delete []states;
880 0 : *aRows = outArray;
881 0 : return rv;
882 : }
883 :
884 : NS_IMETHODIMP
885 0 : nsHTMLTableAccessible::GetCellAt(PRInt32 aRow, PRInt32 aColumn,
886 : nsIAccessible **aTableCellAccessible)
887 : {
888 0 : nsCOMPtr<nsIDOMElement> cellElement;
889 0 : nsresult rv = GetCellAt(aRow, aColumn, *getter_AddRefs(cellElement));
890 0 : NS_ENSURE_SUCCESS(rv, rv);
891 :
892 0 : nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
893 0 : nsAccessible* cell = mDoc->GetAccessible(cellContent);
894 :
895 0 : if (!cell) {
896 0 : return NS_ERROR_INVALID_ARG;
897 : }
898 :
899 0 : if (cell != this) {
900 : // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may
901 : // return itself as a cell what makes Orca hang.
902 0 : NS_ADDREF(*aTableCellAccessible = cell);
903 : }
904 :
905 0 : return NS_OK;
906 : }
907 :
908 : NS_IMETHODIMP
909 0 : nsHTMLTableAccessible::GetCellIndexAt(PRInt32 aRow, PRInt32 aColumn,
910 : PRInt32 *aIndex)
911 : {
912 0 : NS_ENSURE_ARG_POINTER(aIndex);
913 :
914 0 : nsITableLayout *tableLayout = GetTableLayout();
915 0 : NS_ENSURE_STATE(tableLayout);
916 :
917 0 : nsresult rv = tableLayout->GetIndexByRowAndColumn(aRow, aColumn, aIndex);
918 0 : if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND)
919 0 : return NS_ERROR_INVALID_ARG;
920 :
921 0 : return NS_OK;
922 : }
923 :
924 : NS_IMETHODIMP
925 0 : nsHTMLTableAccessible::GetColumnIndexAt(PRInt32 aIndex, PRInt32 *aColumn)
926 : {
927 0 : NS_ENSURE_ARG_POINTER(aColumn);
928 :
929 0 : if (IsDefunct())
930 0 : return NS_ERROR_FAILURE;
931 :
932 0 : nsITableLayout *tableLayout = GetTableLayout();
933 0 : NS_ENSURE_STATE(tableLayout);
934 :
935 : PRInt32 row;
936 0 : nsresult rv = tableLayout->GetRowAndColumnByIndex(aIndex, &row, aColumn);
937 0 : NS_ENSURE_SUCCESS(rv, rv);
938 :
939 0 : return (row == -1 || *aColumn == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
940 : }
941 :
942 : NS_IMETHODIMP
943 0 : nsHTMLTableAccessible::GetRowIndexAt(PRInt32 aIndex, PRInt32 *aRow)
944 : {
945 0 : NS_ENSURE_ARG_POINTER(aRow);
946 :
947 0 : if (IsDefunct())
948 0 : return NS_ERROR_FAILURE;
949 :
950 0 : nsITableLayout *tableLayout = GetTableLayout();
951 0 : NS_ENSURE_STATE(tableLayout);
952 :
953 : PRInt32 column;
954 0 : nsresult rv = tableLayout->GetRowAndColumnByIndex(aIndex, aRow, &column);
955 0 : NS_ENSURE_SUCCESS(rv, rv);
956 :
957 0 : return (*aRow == -1 || column == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
958 : }
959 :
960 : NS_IMETHODIMP
961 0 : nsHTMLTableAccessible::GetRowAndColumnIndicesAt(PRInt32 aIndex,
962 : PRInt32* aRowIdx,
963 : PRInt32* aColumnIdx)
964 : {
965 0 : NS_ENSURE_ARG_POINTER(aRowIdx);
966 0 : *aRowIdx = -1;
967 0 : NS_ENSURE_ARG_POINTER(aColumnIdx);
968 0 : *aColumnIdx = -1;
969 :
970 0 : if (IsDefunct())
971 0 : return NS_ERROR_FAILURE;
972 :
973 0 : nsITableLayout* tableLayout = GetTableLayout();
974 0 : if (tableLayout)
975 0 : tableLayout->GetRowAndColumnByIndex(aIndex, aRowIdx, aColumnIdx);
976 :
977 0 : return (*aRowIdx == -1 || *aColumnIdx == -1) ? NS_ERROR_INVALID_ARG : NS_OK;
978 : }
979 :
980 : NS_IMETHODIMP
981 0 : nsHTMLTableAccessible::GetColumnExtentAt(PRInt32 aRowIndex,
982 : PRInt32 aColumnIndex,
983 : PRInt32 *aExtentCount)
984 : {
985 0 : nsITableLayout *tableLayout = GetTableLayout();
986 0 : NS_ENSURE_STATE(tableLayout);
987 :
988 0 : nsCOMPtr<nsIDOMElement> domElement;
989 : PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan;
990 : bool isSelected;
991 :
992 : nsresult rv = tableLayout->
993 0 : GetCellDataAt(aRowIndex, aColumnIndex, *getter_AddRefs(domElement),
994 : startRowIndex, startColIndex, rowSpan, colSpan,
995 0 : actualRowSpan, *aExtentCount, isSelected);
996 :
997 0 : return (rv == NS_TABLELAYOUT_CELL_NOT_FOUND) ? NS_ERROR_INVALID_ARG : NS_OK;
998 : }
999 :
1000 : NS_IMETHODIMP
1001 0 : nsHTMLTableAccessible::GetRowExtentAt(PRInt32 aRowIndex, PRInt32 aColumnIndex,
1002 : PRInt32 *aExtentCount)
1003 : {
1004 0 : nsITableLayout *tableLayout = GetTableLayout();
1005 0 : NS_ENSURE_STATE(tableLayout);
1006 :
1007 0 : nsCOMPtr<nsIDOMElement> domElement;
1008 : PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualColSpan;
1009 : bool isSelected;
1010 :
1011 : nsresult rv = tableLayout->
1012 0 : GetCellDataAt(aRowIndex, aColumnIndex, *getter_AddRefs(domElement),
1013 : startRowIndex, startColIndex, rowSpan, colSpan,
1014 0 : *aExtentCount, actualColSpan, isSelected);
1015 :
1016 0 : return (rv == NS_TABLELAYOUT_CELL_NOT_FOUND) ? NS_ERROR_INVALID_ARG : NS_OK;
1017 : }
1018 :
1019 : NS_IMETHODIMP
1020 0 : nsHTMLTableAccessible::GetColumnDescription(PRInt32 aColumn, nsAString &_retval)
1021 : {
1022 0 : return NS_ERROR_NOT_IMPLEMENTED;
1023 : }
1024 :
1025 : NS_IMETHODIMP
1026 0 : nsHTMLTableAccessible::GetRowDescription(PRInt32 aRow, nsAString &_retval)
1027 : {
1028 0 : return NS_ERROR_NOT_IMPLEMENTED;
1029 : }
1030 :
1031 : NS_IMETHODIMP
1032 0 : nsHTMLTableAccessible::IsColumnSelected(PRInt32 aColumn, bool *aIsSelected)
1033 : {
1034 0 : NS_ENSURE_ARG_POINTER(aIsSelected);
1035 0 : *aIsSelected = false;
1036 :
1037 0 : PRInt32 colCount = 0;
1038 0 : nsresult rv = GetColumnCount(&colCount);
1039 0 : NS_ENSURE_SUCCESS(rv, rv);
1040 :
1041 0 : if (aColumn < 0 || aColumn >= colCount)
1042 0 : return NS_ERROR_INVALID_ARG;
1043 :
1044 0 : PRInt32 rowCount = 0;
1045 0 : rv = GetRowCount(&rowCount);
1046 0 : NS_ENSURE_SUCCESS(rv, rv);
1047 :
1048 0 : for (PRInt32 rowIdx = 0; rowIdx < rowCount; rowIdx++) {
1049 0 : bool isSelected = false;
1050 0 : rv = IsCellSelected(rowIdx, aColumn, &isSelected);
1051 0 : if (NS_SUCCEEDED(rv)) {
1052 0 : *aIsSelected = isSelected;
1053 0 : if (!isSelected)
1054 0 : break;
1055 : }
1056 : }
1057 :
1058 0 : return NS_OK;
1059 : }
1060 :
1061 : NS_IMETHODIMP
1062 0 : nsHTMLTableAccessible::IsRowSelected(PRInt32 aRow, bool *aIsSelected)
1063 : {
1064 0 : NS_ENSURE_ARG_POINTER(aIsSelected);
1065 0 : *aIsSelected = false;
1066 :
1067 0 : PRInt32 rowCount = 0;
1068 0 : nsresult rv = GetRowCount(&rowCount);
1069 0 : NS_ENSURE_SUCCESS(rv, rv);
1070 :
1071 0 : if (aRow < 0 || aRow >= rowCount)
1072 0 : return NS_ERROR_INVALID_ARG;
1073 :
1074 0 : PRInt32 colCount = 0;
1075 0 : rv = GetColumnCount(&colCount);
1076 0 : NS_ENSURE_SUCCESS(rv, rv);
1077 :
1078 0 : for (PRInt32 colIdx = 0; colIdx < colCount; colIdx++) {
1079 0 : bool isSelected = false;
1080 0 : rv = IsCellSelected(aRow, colIdx, &isSelected);
1081 0 : if (NS_SUCCEEDED(rv)) {
1082 0 : *aIsSelected = isSelected;
1083 0 : if (!isSelected)
1084 0 : break;
1085 : }
1086 : }
1087 :
1088 0 : return NS_OK;
1089 : }
1090 :
1091 : NS_IMETHODIMP
1092 0 : nsHTMLTableAccessible::IsCellSelected(PRInt32 aRow, PRInt32 aColumn,
1093 : bool *aIsSelected)
1094 : {
1095 0 : NS_ENSURE_ARG_POINTER(aIsSelected);
1096 0 : *aIsSelected = false;
1097 :
1098 0 : nsITableLayout *tableLayout = GetTableLayout();
1099 0 : NS_ENSURE_STATE(tableLayout);
1100 :
1101 0 : nsCOMPtr<nsIDOMElement> domElement;
1102 0 : PRInt32 startRowIndex = 0, startColIndex = 0,
1103 : rowSpan, colSpan, actualRowSpan, actualColSpan;
1104 :
1105 : nsresult rv = tableLayout->
1106 0 : GetCellDataAt(aRow, aColumn, *getter_AddRefs(domElement),
1107 : startRowIndex, startColIndex, rowSpan, colSpan,
1108 0 : actualRowSpan, actualColSpan, *aIsSelected);
1109 :
1110 0 : if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND)
1111 0 : return NS_ERROR_INVALID_ARG;
1112 0 : return rv;
1113 : }
1114 :
1115 : NS_IMETHODIMP
1116 0 : nsHTMLTableAccessible::SelectRow(PRInt32 aRow)
1117 : {
1118 0 : if (IsDefunct())
1119 0 : return NS_ERROR_FAILURE;
1120 :
1121 : nsresult rv =
1122 : RemoveRowsOrColumnsFromSelection(aRow,
1123 : nsISelectionPrivate::TABLESELECTION_ROW,
1124 0 : true);
1125 0 : NS_ENSURE_SUCCESS(rv, rv);
1126 :
1127 : return AddRowOrColumnToSelection(aRow,
1128 0 : nsISelectionPrivate::TABLESELECTION_ROW);
1129 : }
1130 :
1131 : NS_IMETHODIMP
1132 0 : nsHTMLTableAccessible::SelectColumn(PRInt32 aColumn)
1133 : {
1134 0 : if (IsDefunct())
1135 0 : return NS_ERROR_FAILURE;
1136 :
1137 : nsresult rv =
1138 : RemoveRowsOrColumnsFromSelection(aColumn,
1139 : nsISelectionPrivate::TABLESELECTION_COLUMN,
1140 0 : true);
1141 0 : NS_ENSURE_SUCCESS(rv, rv);
1142 :
1143 : return AddRowOrColumnToSelection(aColumn,
1144 0 : nsISelectionPrivate::TABLESELECTION_COLUMN);
1145 : }
1146 :
1147 : NS_IMETHODIMP
1148 0 : nsHTMLTableAccessible::UnselectRow(PRInt32 aRow)
1149 : {
1150 0 : if (IsDefunct())
1151 0 : return NS_ERROR_FAILURE;
1152 :
1153 : return
1154 : RemoveRowsOrColumnsFromSelection(aRow,
1155 : nsISelectionPrivate::TABLESELECTION_ROW,
1156 0 : false);
1157 : }
1158 :
1159 : NS_IMETHODIMP
1160 0 : nsHTMLTableAccessible::UnselectColumn(PRInt32 aColumn)
1161 : {
1162 0 : if (IsDefunct())
1163 0 : return NS_ERROR_FAILURE;
1164 :
1165 : return
1166 : RemoveRowsOrColumnsFromSelection(aColumn,
1167 : nsISelectionPrivate::TABLESELECTION_COLUMN,
1168 0 : false);
1169 : }
1170 :
1171 : nsresult
1172 0 : nsHTMLTableAccessible::AddRowOrColumnToSelection(PRInt32 aIndex,
1173 : PRUint32 aTarget)
1174 : {
1175 0 : bool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
1176 :
1177 0 : nsITableLayout *tableLayout = GetTableLayout();
1178 0 : NS_ENSURE_STATE(tableLayout);
1179 :
1180 0 : nsCOMPtr<nsIDOMElement> cellElm;
1181 : PRInt32 startRowIdx, startColIdx, rowSpan, colSpan,
1182 : actualRowSpan, actualColSpan;
1183 0 : bool isSelected = false;
1184 :
1185 0 : nsresult rv = NS_OK;
1186 0 : PRInt32 count = 0;
1187 0 : if (doSelectRow)
1188 0 : rv = GetColumnCount(&count);
1189 : else
1190 0 : rv = GetRowCount(&count);
1191 :
1192 0 : NS_ENSURE_SUCCESS(rv, rv);
1193 :
1194 0 : nsIPresShell* presShell(mDoc->PresShell());
1195 : nsRefPtr<nsFrameSelection> tableSelection =
1196 0 : const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
1197 :
1198 0 : for (PRInt32 idx = 0; idx < count; idx++) {
1199 0 : PRInt32 rowIdx = doSelectRow ? aIndex : idx;
1200 0 : PRInt32 colIdx = doSelectRow ? idx : aIndex;
1201 : rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
1202 0 : *getter_AddRefs(cellElm),
1203 : startRowIdx, startColIdx,
1204 : rowSpan, colSpan,
1205 : actualRowSpan, actualColSpan,
1206 0 : isSelected);
1207 :
1208 0 : if (NS_SUCCEEDED(rv) && !isSelected) {
1209 0 : nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm));
1210 0 : rv = tableSelection->SelectCellElement(cellContent);
1211 0 : NS_ENSURE_SUCCESS(rv, rv);
1212 : }
1213 : }
1214 :
1215 0 : return NS_OK;
1216 : }
1217 :
1218 : nsresult
1219 0 : nsHTMLTableAccessible::RemoveRowsOrColumnsFromSelection(PRInt32 aIndex,
1220 : PRUint32 aTarget,
1221 : bool aIsOuter)
1222 : {
1223 0 : nsITableLayout *tableLayout = GetTableLayout();
1224 0 : NS_ENSURE_STATE(tableLayout);
1225 :
1226 0 : nsIPresShell* presShell(mDoc->PresShell());
1227 : nsRefPtr<nsFrameSelection> tableSelection =
1228 0 : const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
1229 :
1230 0 : bool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
1231 0 : PRInt32 count = 0;
1232 0 : nsresult rv = doUnselectRow ? GetColumnCount(&count) : GetRowCount(&count);
1233 0 : NS_ENSURE_SUCCESS(rv, rv);
1234 :
1235 0 : PRInt32 startRowIdx = doUnselectRow ? aIndex : 0;
1236 0 : PRInt32 endRowIdx = doUnselectRow ? aIndex : count - 1;
1237 0 : PRInt32 startColIdx = doUnselectRow ? 0 : aIndex;
1238 0 : PRInt32 endColIdx = doUnselectRow ? count - 1 : aIndex;
1239 :
1240 0 : if (aIsOuter)
1241 0 : return tableSelection->RestrictCellsToSelection(mContent,
1242 : startRowIdx, startColIdx,
1243 0 : endRowIdx, endColIdx);
1244 :
1245 0 : return tableSelection->RemoveCellsFromSelection(mContent,
1246 : startRowIdx, startColIdx,
1247 0 : endRowIdx, endColIdx);
1248 : }
1249 :
1250 : nsITableLayout*
1251 0 : nsHTMLTableAccessible::GetTableLayout()
1252 : {
1253 0 : nsIFrame *frame = mContent->GetPrimaryFrame();
1254 0 : if (!frame)
1255 0 : return nsnull;
1256 :
1257 0 : nsITableLayout *tableLayout = do_QueryFrame(frame);
1258 0 : return tableLayout;
1259 : }
1260 :
1261 : nsresult
1262 0 : nsHTMLTableAccessible::GetCellAt(PRInt32 aRowIndex,
1263 : PRInt32 aColIndex,
1264 : nsIDOMElement* &aCell)
1265 : {
1266 0 : PRInt32 startRowIndex = 0, startColIndex = 0,
1267 : rowSpan, colSpan, actualRowSpan, actualColSpan;
1268 : bool isSelected;
1269 :
1270 0 : nsITableLayout *tableLayout = GetTableLayout();
1271 0 : NS_ENSURE_STATE(tableLayout);
1272 :
1273 : nsresult rv = tableLayout->
1274 : GetCellDataAt(aRowIndex, aColIndex, aCell, startRowIndex, startColIndex,
1275 0 : rowSpan, colSpan, actualRowSpan, actualColSpan, isSelected);
1276 :
1277 0 : if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND)
1278 0 : return NS_ERROR_INVALID_ARG;
1279 0 : return rv;
1280 : }
1281 :
1282 : void
1283 0 : nsHTMLTableAccessible::Description(nsString& aDescription)
1284 : {
1285 : // Helpful for debugging layout vs. data tables
1286 0 : aDescription.Truncate();
1287 0 : nsAccessible::Description(aDescription);
1288 0 : if (!aDescription.IsEmpty())
1289 0 : return;
1290 :
1291 : // Use summary as description if it weren't used as a name.
1292 : // XXX: get rid code duplication with NameInternal().
1293 0 : nsAccessible* caption = Caption();
1294 0 : if (caption) {
1295 0 : nsIContent* captionContent = caption->GetContent();
1296 0 : if (captionContent) {
1297 0 : nsAutoString captionText;
1298 : nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent,
1299 0 : &captionText);
1300 :
1301 0 : if (!captionText.IsEmpty()) { // summary isn't used as a name.
1302 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary,
1303 0 : aDescription);
1304 : }
1305 : }
1306 : }
1307 :
1308 : #ifdef SHOW_LAYOUT_HEURISTIC
1309 : if (aDescription.IsEmpty()) {
1310 : bool isProbablyForLayout;
1311 : IsProbablyForLayout(&isProbablyForLayout);
1312 : aDescription = mLayoutHeuristic;
1313 : }
1314 : #ifdef DEBUG_A11Y
1315 : printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get());
1316 : #endif
1317 : #endif
1318 : }
1319 :
1320 : bool
1321 0 : nsHTMLTableAccessible::HasDescendant(const nsAString& aTagName,
1322 : bool aAllowEmpty)
1323 : {
1324 0 : nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mContent));
1325 0 : NS_ENSURE_TRUE(tableElt, false);
1326 :
1327 0 : nsCOMPtr<nsIDOMNodeList> nodeList;
1328 0 : tableElt->GetElementsByTagName(aTagName, getter_AddRefs(nodeList));
1329 0 : NS_ENSURE_TRUE(nodeList, false);
1330 :
1331 0 : nsCOMPtr<nsIDOMNode> foundItem;
1332 0 : nodeList->Item(0, getter_AddRefs(foundItem));
1333 0 : if (!foundItem)
1334 0 : return false;
1335 :
1336 0 : if (aAllowEmpty)
1337 0 : return true;
1338 :
1339 : // Make sure that the item we found has contents and either has multiple
1340 : // children or the found item is not a whitespace-only text node.
1341 0 : nsCOMPtr<nsIContent> foundItemContent = do_QueryInterface(foundItem);
1342 0 : if (foundItemContent->GetChildCount() > 1)
1343 0 : return true; // Treat multiple child nodes as non-empty
1344 :
1345 0 : nsIContent *innerItemContent = foundItemContent->GetFirstChild();
1346 0 : if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
1347 0 : return true;
1348 :
1349 : // If we found more than one node then return true not depending on
1350 : // aAllowEmpty flag.
1351 : // XXX it might be dummy but bug 501375 where we changed this addresses
1352 : // performance problems only. Note, currently 'aAllowEmpty' flag is used for
1353 : // caption element only. On another hand we create accessible object for
1354 : // the first entry of caption element (see
1355 : // nsHTMLTableAccessible::CacheChildren).
1356 0 : nodeList->Item(1, getter_AddRefs(foundItem));
1357 0 : return !!foundItem;
1358 : }
1359 :
1360 : NS_IMETHODIMP
1361 0 : nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
1362 : {
1363 : // Implement a heuristic to determine if table is most likely used for layout
1364 : // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells
1365 : // at the beginning or end of a row/col, and especially when they occur at the edge of a table?
1366 : // XXX expose this info via object attributes to AT-SPI
1367 :
1368 : // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
1369 : // This will allow release trunk builds to be used by testers to refine the algorithm
1370 : // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
1371 : #ifdef SHOW_LAYOUT_HEURISTIC
1372 : #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
1373 : { *aIsProbablyForLayout = isLayout; \
1374 : mLayoutHeuristic = isLayout ? NS_LITERAL_STRING("layout table: ") : NS_LITERAL_STRING("data table: "); \
1375 : mLayoutHeuristic += NS_LITERAL_STRING(heuristic); return NS_OK; }
1376 : #else
1377 : #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; }
1378 : #endif
1379 :
1380 0 : *aIsProbablyForLayout = false;
1381 :
1382 0 : if (IsDefunct())
1383 0 : return NS_ERROR_FAILURE;
1384 :
1385 0 : nsDocAccessible* docAccessible = Document();
1386 0 : if (docAccessible) {
1387 0 : PRUint64 docState = docAccessible->State();
1388 0 : if (docState & states::EDITABLE) { // Need to see all elements while document is being edited
1389 0 : RETURN_LAYOUT_ANSWER(false, "In editable document");
1390 : }
1391 : }
1392 :
1393 : // Check to see if an ARIA role overrides the role from native markup,
1394 : // but for which we still expose table semantics (treegrid, for example).
1395 0 : if (Role() != roles::TABLE)
1396 0 : RETURN_LAYOUT_ANSWER(false, "Has role attribute");
1397 :
1398 0 : if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
1399 : // Role attribute is present, but overridden roles have already been dealt with.
1400 : // Only landmarks and other roles that don't override the role from native
1401 : // markup are left to deal with here.
1402 0 : RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
1403 : }
1404 :
1405 0 : if (mContent->Tag() != nsGkAtoms::table)
1406 0 : RETURN_LAYOUT_ANSWER(true, "table built by CSS display:table style");
1407 :
1408 : // Check if datatable attribute has "0" value.
1409 0 : if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
1410 0 : NS_LITERAL_STRING("0"), eCaseMatters)) {
1411 0 : RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
1412 : }
1413 :
1414 : // Check for legitimate data table attributes.
1415 0 : nsAutoString summary;
1416 0 : if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
1417 0 : !summary.IsEmpty())
1418 0 : RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
1419 :
1420 : // Check for legitimate data table elements.
1421 0 : nsAccessible* caption = FirstChild();
1422 0 : if (caption && caption->Role() == roles::CAPTION && caption->HasChildren())
1423 0 : RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
1424 :
1425 0 : for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
1426 0 : childElm = childElm->GetNextSibling()) {
1427 0 : if (!childElm->IsHTML())
1428 0 : continue;
1429 :
1430 0 : if (childElm->Tag() == nsGkAtoms::col ||
1431 0 : childElm->Tag() == nsGkAtoms::colgroup ||
1432 0 : childElm->Tag() == nsGkAtoms::tfoot ||
1433 0 : childElm->Tag() == nsGkAtoms::thead) {
1434 0 : RETURN_LAYOUT_ANSWER(false,
1435 : "Has col, colgroup, tfoot or thead -- legitimate table structures");
1436 : }
1437 :
1438 0 : if (childElm->Tag() == nsGkAtoms::tbody) {
1439 0 : for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
1440 0 : rowElm = rowElm->GetNextSibling()) {
1441 0 : if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) {
1442 0 : for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
1443 0 : cellElm = cellElm->GetNextSibling()) {
1444 0 : if (cellElm->IsHTML()) {
1445 :
1446 0 : if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
1447 0 : RETURN_LAYOUT_ANSWER(false,
1448 : "Has th -- legitimate table structures");
1449 : }
1450 :
1451 0 : if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
1452 0 : cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
1453 0 : cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
1454 0 : RETURN_LAYOUT_ANSWER(false,
1455 : "Has headers, scope, or abbr attribute -- legitimate table structures");
1456 : }
1457 :
1458 0 : nsAccessible* cell = mDoc->GetAccessible(cellElm);
1459 0 : if (cell && cell->GetChildCount() == 1 &&
1460 0 : cell->FirstChild()->IsAbbreviation()) {
1461 0 : RETURN_LAYOUT_ANSWER(false,
1462 : "has abbr -- legitimate table structures");
1463 : }
1464 : }
1465 : }
1466 : }
1467 : }
1468 : }
1469 : }
1470 :
1471 0 : if (HasDescendant(NS_LITERAL_STRING("table"))) {
1472 0 : RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
1473 : }
1474 :
1475 : // If only 1 column or only 1 row, it's for layout
1476 : PRInt32 columns, rows;
1477 0 : GetColumnCount(&columns);
1478 0 : if (columns <=1) {
1479 0 : RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
1480 : }
1481 0 : GetRowCount(&rows);
1482 0 : if (rows <=1) {
1483 0 : RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
1484 : }
1485 :
1486 : // Check for many columns
1487 0 : if (columns >= 5) {
1488 0 : RETURN_LAYOUT_ANSWER(false, ">=5 columns");
1489 : }
1490 :
1491 : // Now we know there are 2-4 columns and 2 or more rows
1492 : // Check to see if there are visible borders on the cells
1493 : // XXX currently, we just check the first cell -- do we really need to do more?
1494 0 : nsCOMPtr<nsIDOMElement> cellElement;
1495 0 : nsresult rv = GetCellAt(0, 0, *getter_AddRefs(cellElement));
1496 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1497 :
1498 0 : nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElement));
1499 0 : NS_ENSURE_TRUE(cellContent, NS_ERROR_FAILURE);
1500 0 : nsIFrame *cellFrame = cellContent->GetPrimaryFrame();
1501 0 : if (!cellFrame) {
1502 0 : return NS_OK;
1503 : }
1504 0 : nsMargin border;
1505 0 : cellFrame->GetBorder(border);
1506 0 : if (border.top && border.bottom && border.left && border.right) {
1507 0 : RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
1508 : }
1509 :
1510 : /**
1511 : * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
1512 : */
1513 :
1514 : // Check for styled background color across rows (alternating background
1515 : // color is a common feature for data tables).
1516 0 : PRUint32 childCount = GetChildCount();
1517 : nscolor rowColor, prevRowColor;
1518 0 : for (PRUint32 childIdx = 0; childIdx < childCount; childIdx++) {
1519 0 : nsAccessible* child = GetChildAt(childIdx);
1520 0 : if (child->Role() == roles::ROW) {
1521 0 : prevRowColor = rowColor;
1522 0 : nsIFrame* rowFrame = child->GetFrame();
1523 0 : rowColor = rowFrame->GetStyleBackground()->mBackgroundColor;
1524 :
1525 0 : if (childIdx > 0 && prevRowColor != rowColor)
1526 0 : RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
1527 : }
1528 : }
1529 :
1530 : // Check for many rows
1531 0 : const PRInt32 kMaxLayoutRows = 20;
1532 0 : if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data
1533 0 : RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
1534 : }
1535 :
1536 : // Check for very wide table.
1537 0 : nsIFrame* documentFrame = Document()->GetFrame();
1538 0 : nsSize documentSize = documentFrame->GetSize();
1539 0 : if (documentSize.width > 0) {
1540 0 : nsSize tableSize = GetFrame()->GetSize();
1541 0 : PRInt32 percentageOfDocWidth = (100 * tableSize.width) / documentSize.width;
1542 0 : if (percentageOfDocWidth > 95) {
1543 : // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
1544 : // Probably for layout
1545 0 : RETURN_LAYOUT_ANSWER(true,
1546 : "<= 4 columns, table width is 95% of document width");
1547 : }
1548 : }
1549 :
1550 : // Two column rules
1551 0 : if (rows * columns <= 10) {
1552 0 : RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
1553 : }
1554 :
1555 0 : if (HasDescendant(NS_LITERAL_STRING("embed")) ||
1556 0 : HasDescendant(NS_LITERAL_STRING("object")) ||
1557 0 : HasDescendant(NS_LITERAL_STRING("applet")) ||
1558 0 : HasDescendant(NS_LITERAL_STRING("iframe"))) {
1559 0 : RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
1560 : }
1561 :
1562 0 : RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
1563 : }
1564 :
1565 :
1566 : ////////////////////////////////////////////////////////////////////////////////
1567 : // nsHTMLCaptionAccessible
1568 : ////////////////////////////////////////////////////////////////////////////////
1569 :
1570 : Relation
1571 0 : nsHTMLCaptionAccessible::RelationByType(PRUint32 aType)
1572 : {
1573 0 : Relation rel = nsHyperTextAccessible::RelationByType(aType);
1574 0 : if (aType == nsIAccessibleRelation::RELATION_LABEL_FOR)
1575 0 : rel.AppendTarget(Parent());
1576 :
1577 : return rel;
1578 : }
1579 :
1580 : role
1581 0 : nsHTMLCaptionAccessible::NativeRole()
1582 : {
1583 0 : return roles::CAPTION;
1584 : }
|