| Index: components/autofill/content/renderer/form_autofill_util.cc
|
| diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
|
| index d7a163c940ce59c30068fe47ec24778bb1128710..5977300794df57f4e2269a4affb18d958c5abf51 100644
|
| --- a/components/autofill/content/renderer/form_autofill_util.cc
|
| +++ b/components/autofill/content/renderer/form_autofill_util.cc
|
| @@ -10,6 +10,7 @@
|
| #include "base/command_line.h"
|
| #include "base/logging.h"
|
| #include "base/memory/scoped_vector.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| #include "components/autofill/core/common/autofill_data_validation.h"
|
| @@ -133,6 +134,24 @@ bool SatisfiesRequireAutocomplete(const WebInputElement& input_element) {
|
| return input_element.autoComplete();
|
| }
|
|
|
| +// Returns the colspan for a <td>. Defaults to 1.
|
| +size_t CalculateTableCellColumnSpan(const WebElement& element) {
|
| + DCHECK(element.hasHTMLTagName("td"));
|
| +
|
| + size_t span = 1;
|
| + if (element.hasAttribute("colspan")) {
|
| + base::string16 colspan = element.getAttribute("colspan");
|
| + // Do not check return value to accept imperfect conversions.
|
| + base::StringToSizeT(colspan, &span);
|
| + // Handle overflow.
|
| + if (span == std::numeric_limits<size_t>::max())
|
| + span = 1;
|
| + span = std::max(span, static_cast<size_t>(1));
|
| + }
|
| +
|
| + return span;
|
| +}
|
| +
|
| // Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed
|
| // to a single space. If |force_whitespace| is true, then the resulting string
|
| // is guaranteed to have a space between |prefix| and |suffix|. Otherwise, the
|
| @@ -392,8 +411,62 @@ base::string16 InferLabelFromTableColumn(const WebFormControlElement& element) {
|
|
|
| // Helper for |InferLabelForElement()| that infers a label, if possible, from
|
| // surrounding table structure,
|
| +//
|
| +// If there are multiple cells and the row with the input matches up with the
|
| +// previous row, then look for a specific cell within the previous row.
|
| +// e.g. <tr><td>Input 1 label</td><td>Input 2 label</td></tr>
|
| +// <tr><td><input name="input 1"></td><td><input name="input2"></td></tr>
|
| +//
|
| +// Otherwise, just look in the entire previous row.
|
| // e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr>
|
| base::string16 InferLabelFromTableRow(const WebFormControlElement& element) {
|
| + CR_DEFINE_STATIC_LOCAL(WebString, kTableCell, ("td"));
|
| + base::string16 inferred_label;
|
| +
|
| + // First find the <td> that contains |element|.
|
| + WebNode cell = element.parentNode();
|
| + while (!cell.isNull()) {
|
| + if (cell.isElementNode() &&
|
| + cell.to<WebElement>().hasHTMLTagName(kTableCell)) {
|
| + break;
|
| + }
|
| + cell = cell.parentNode();
|
| + }
|
| +
|
| + // Not in a cell - bail out.
|
| + if (cell.isNull())
|
| + return inferred_label;
|
| +
|
| + // Count the cell holding |element|.
|
| + size_t cell_count = CalculateTableCellColumnSpan(cell.to<WebElement>());
|
| + size_t cell_position = 0;
|
| + size_t cell_position_end = cell_count - 1;
|
| +
|
| + // Count cells to the left to figure out |element|'s cell's position.
|
| + for (WebNode cell_it = cell.previousSibling();
|
| + !cell_it.isNull();
|
| + cell_it = cell_it.previousSibling()) {
|
| + if (cell_it.isElementNode() &&
|
| + cell_it.to<WebElement>().hasHTMLTagName(kTableCell)) {
|
| + cell_position += CalculateTableCellColumnSpan(cell_it.to<WebElement>());
|
| + }
|
| + }
|
| +
|
| + // Count cells to the right.
|
| + for (WebNode cell_it = cell.nextSibling();
|
| + !cell_it.isNull();
|
| + cell_it = cell_it.nextSibling()) {
|
| + if (cell_it.isElementNode() &&
|
| + cell_it.to<WebElement>().hasHTMLTagName(kTableCell)) {
|
| + cell_count += CalculateTableCellColumnSpan(cell_it.to<WebElement>());
|
| + }
|
| + }
|
| +
|
| + // Combine left + right.
|
| + cell_count += cell_position;
|
| + cell_position_end += cell_position;
|
| +
|
| + // Find the current row.
|
| CR_DEFINE_STATIC_LOCAL(WebString, kTableRow, ("tr"));
|
| WebNode parent = element.parentNode();
|
| while (!parent.isNull() && parent.isElementNode() &&
|
| @@ -402,11 +475,51 @@ base::string16 InferLabelFromTableRow(const WebFormControlElement& element) {
|
| }
|
|
|
| if (parent.isNull())
|
| - return base::string16();
|
| + return inferred_label;
|
|
|
| - // Check all previous siblings, skipping non-element nodes, until we find a
|
| - // non-empty text block.
|
| - base::string16 inferred_label;
|
| + // Now find the previous row.
|
| + WebNode row_it = parent.previousSibling();
|
| + while (!row_it.isNull()) {
|
| + if (row_it.isElementNode() &&
|
| + row_it.to<WebElement>().hasHTMLTagName(kTableRow)) {
|
| + break;
|
| + }
|
| + row_it = row_it.previousSibling();
|
| + }
|
| +
|
| + // If there exists a previous row, check its cells and size. If they align
|
| + // with the current row, infer the label from the cell above.
|
| + if (!row_it.isNull()) {
|
| + WebNode matching_cell;
|
| + size_t prev_row_count = 0;
|
| + WebNode prev_row_it = row_it.firstChild();
|
| + CR_DEFINE_STATIC_LOCAL(WebString, kTableHeader, ("th"));
|
| + while (!prev_row_it.isNull()) {
|
| + if (prev_row_it.isElementNode()) {
|
| + WebElement prev_row_element = prev_row_it.to<WebElement>();
|
| + if (prev_row_element.hasHTMLTagName(kTableCell) ||
|
| + prev_row_element.hasHTMLTagName(kTableHeader)) {
|
| + size_t span = CalculateTableCellColumnSpan(prev_row_element);
|
| + size_t prev_row_count_end = prev_row_count + span - 1;
|
| + if (prev_row_count == cell_position &&
|
| + prev_row_count_end == cell_position_end) {
|
| + matching_cell = prev_row_it;
|
| + }
|
| + prev_row_count += span;
|
| + }
|
| + }
|
| + prev_row_it = prev_row_it.nextSibling();
|
| + }
|
| + if ((cell_count == prev_row_count) && !matching_cell.isNull()) {
|
| + inferred_label = FindChildText(matching_cell);
|
| + if (!inferred_label.empty())
|
| + return inferred_label;
|
| + }
|
| + }
|
| +
|
| + // If there is no previous row, or if the previous row and current row do not
|
| + // align, check all previous siblings, skipping non-element nodes, until we
|
| + // find a non-empty text block.
|
| WebNode previous = parent.previousSibling();
|
| while (inferred_label.empty() && !previous.isNull()) {
|
| if (HasTagName(previous, kTableRow))
|
|
|