| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/autofill/renderer/form_autofill_util.h" | 5 #include "components/autofill/renderer/form_autofill_util.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 | 8 |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 // has leading whitespace. | 97 // has leading whitespace. |
| 98 // A few examples: | 98 // A few examples: |
| 99 // * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar" | 99 // * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar" |
| 100 // * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar" | 100 // * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar" |
| 101 // * CombineAndCollapseWhitespace("foo ", "bar", false) -> "foo bar" | 101 // * CombineAndCollapseWhitespace("foo ", "bar", false) -> "foo bar" |
| 102 // * CombineAndCollapseWhitespace("foo", " bar", false) -> "foo bar" | 102 // * CombineAndCollapseWhitespace("foo", " bar", false) -> "foo bar" |
| 103 // * CombineAndCollapseWhitespace("foo", " bar", true) -> "foo bar" | 103 // * CombineAndCollapseWhitespace("foo", " bar", true) -> "foo bar" |
| 104 // * CombineAndCollapseWhitespace("foo ", " bar", false) -> "foo bar" | 104 // * CombineAndCollapseWhitespace("foo ", " bar", false) -> "foo bar" |
| 105 // * CombineAndCollapseWhitespace(" foo", "bar ", false) -> " foobar " | 105 // * CombineAndCollapseWhitespace(" foo", "bar ", false) -> " foobar " |
| 106 // * CombineAndCollapseWhitespace(" foo", "bar ", true) -> " foo bar " | 106 // * CombineAndCollapseWhitespace(" foo", "bar ", true) -> " foo bar " |
| 107 const string16 CombineAndCollapseWhitespace(const string16& prefix, | 107 const base::string16 CombineAndCollapseWhitespace( |
| 108 const string16& suffix, | 108 const base::string16& prefix, |
| 109 bool force_whitespace) { | 109 const base::string16& suffix, |
| 110 string16 prefix_trimmed; | 110 bool force_whitespace) { |
| 111 base::string16 prefix_trimmed; |
| 111 TrimPositions prefix_trailing_whitespace = | 112 TrimPositions prefix_trailing_whitespace = |
| 112 TrimWhitespace(prefix, TRIM_TRAILING, &prefix_trimmed); | 113 TrimWhitespace(prefix, TRIM_TRAILING, &prefix_trimmed); |
| 113 | 114 |
| 114 // Recursively compute the children's text. | 115 // Recursively compute the children's text. |
| 115 string16 suffix_trimmed; | 116 base::string16 suffix_trimmed; |
| 116 TrimPositions suffix_leading_whitespace = | 117 TrimPositions suffix_leading_whitespace = |
| 117 TrimWhitespace(suffix, TRIM_LEADING, &suffix_trimmed); | 118 TrimWhitespace(suffix, TRIM_LEADING, &suffix_trimmed); |
| 118 | 119 |
| 119 if (prefix_trailing_whitespace || suffix_leading_whitespace || | 120 if (prefix_trailing_whitespace || suffix_leading_whitespace || |
| 120 force_whitespace) { | 121 force_whitespace) { |
| 121 return prefix_trimmed + ASCIIToUTF16(" ") + suffix_trimmed; | 122 return prefix_trimmed + ASCIIToUTF16(" ") + suffix_trimmed; |
| 122 } else { | 123 } else { |
| 123 return prefix_trimmed + suffix_trimmed; | 124 return prefix_trimmed + suffix_trimmed; |
| 124 } | 125 } |
| 125 } | 126 } |
| 126 | 127 |
| 127 // This is a helper function for the FindChildText() function (see below). | 128 // This is a helper function for the FindChildText() function (see below). |
| 128 // Search depth is limited with the |depth| parameter. | 129 // Search depth is limited with the |depth| parameter. |
| 129 string16 FindChildTextInner(const WebNode& node, int depth) { | 130 base::string16 FindChildTextInner(const WebNode& node, int depth) { |
| 130 if (depth <= 0 || node.isNull()) | 131 if (depth <= 0 || node.isNull()) |
| 131 return string16(); | 132 return base::string16(); |
| 132 | 133 |
| 133 // Skip over comments. | 134 // Skip over comments. |
| 134 if (node.nodeType() == WebNode::CommentNode) | 135 if (node.nodeType() == WebNode::CommentNode) |
| 135 return FindChildTextInner(node.nextSibling(), depth - 1); | 136 return FindChildTextInner(node.nextSibling(), depth - 1); |
| 136 | 137 |
| 137 if (node.nodeType() != WebNode::ElementNode && | 138 if (node.nodeType() != WebNode::ElementNode && |
| 138 node.nodeType() != WebNode::TextNode) | 139 node.nodeType() != WebNode::TextNode) |
| 139 return string16(); | 140 return base::string16(); |
| 140 | 141 |
| 141 // Ignore elements known not to contain inferable labels. | 142 // Ignore elements known not to contain inferable labels. |
| 142 if (node.isElementNode()) { | 143 if (node.isElementNode()) { |
| 143 const WebElement element = node.toConst<WebElement>(); | 144 const WebElement element = node.toConst<WebElement>(); |
| 144 if (IsOptionElement(element) || | 145 if (IsOptionElement(element) || |
| 145 IsScriptElement(element) || | 146 IsScriptElement(element) || |
| 146 IsNoScriptElement(element) || | 147 IsNoScriptElement(element) || |
| 147 (element.isFormControlElement() && | 148 (element.isFormControlElement() && |
| 148 IsAutofillableElement(element.toConst<WebFormControlElement>()))) { | 149 IsAutofillableElement(element.toConst<WebFormControlElement>()))) { |
| 149 return string16(); | 150 return base::string16(); |
| 150 } | 151 } |
| 151 } | 152 } |
| 152 | 153 |
| 153 // Extract the text exactly at this node. | 154 // Extract the text exactly at this node. |
| 154 string16 node_text = node.nodeValue(); | 155 base::string16 node_text = node.nodeValue(); |
| 155 | 156 |
| 156 // Recursively compute the children's text. | 157 // Recursively compute the children's text. |
| 157 // Preserve inter-element whitespace separation. | 158 // Preserve inter-element whitespace separation. |
| 158 string16 child_text = FindChildTextInner(node.firstChild(), depth - 1); | 159 base::string16 child_text = FindChildTextInner(node.firstChild(), depth - 1); |
| 159 bool add_space = node.nodeType() == WebNode::TextNode && node_text.empty(); | 160 bool add_space = node.nodeType() == WebNode::TextNode && node_text.empty(); |
| 160 node_text = CombineAndCollapseWhitespace(node_text, child_text, add_space); | 161 node_text = CombineAndCollapseWhitespace(node_text, child_text, add_space); |
| 161 | 162 |
| 162 // Recursively compute the siblings' text. | 163 // Recursively compute the siblings' text. |
| 163 // Again, preserve inter-element whitespace separation. | 164 // Again, preserve inter-element whitespace separation. |
| 164 string16 sibling_text = FindChildTextInner(node.nextSibling(), depth - 1); | 165 base::string16 sibling_text = |
| 166 FindChildTextInner(node.nextSibling(), depth - 1); |
| 165 add_space = node.nodeType() == WebNode::TextNode && node_text.empty(); | 167 add_space = node.nodeType() == WebNode::TextNode && node_text.empty(); |
| 166 node_text = CombineAndCollapseWhitespace(node_text, sibling_text, add_space); | 168 node_text = CombineAndCollapseWhitespace(node_text, sibling_text, add_space); |
| 167 | 169 |
| 168 return node_text; | 170 return node_text; |
| 169 } | 171 } |
| 170 | 172 |
| 171 // Returns the aggregated values of the descendants of |element| that are | 173 // Returns the aggregated values of the descendants of |element| that are |
| 172 // non-empty text nodes. This is a faster alternative to |innerText()| for | 174 // non-empty text nodes. This is a faster alternative to |innerText()| for |
| 173 // performance critical operations. It does a full depth-first search so can be | 175 // performance critical operations. It does a full depth-first search so can be |
| 174 // used when the structure is not directly known. However, unlike with | 176 // used when the structure is not directly known. However, unlike with |
| 175 // |innerText()|, the search depth and breadth are limited to a fixed threshold. | 177 // |innerText()|, the search depth and breadth are limited to a fixed threshold. |
| 176 // Whitespace is trimmed from text accumulated at descendant nodes. | 178 // Whitespace is trimmed from text accumulated at descendant nodes. |
| 177 string16 FindChildText(const WebNode& node) { | 179 base::string16 FindChildText(const WebNode& node) { |
| 178 if (node.isTextNode()) | 180 if (node.isTextNode()) |
| 179 return node.nodeValue(); | 181 return node.nodeValue(); |
| 180 | 182 |
| 181 WebNode child = node.firstChild(); | 183 WebNode child = node.firstChild(); |
| 182 | 184 |
| 183 const int kChildSearchDepth = 10; | 185 const int kChildSearchDepth = 10; |
| 184 string16 node_text = FindChildTextInner(child, kChildSearchDepth); | 186 base::string16 node_text = FindChildTextInner(child, kChildSearchDepth); |
| 185 TrimWhitespace(node_text, TRIM_ALL, &node_text); | 187 TrimWhitespace(node_text, TRIM_ALL, &node_text); |
| 186 return node_text; | 188 return node_text; |
| 187 } | 189 } |
| 188 | 190 |
| 189 // Helper for |InferLabelForElement()| that infers a label, if possible, from | 191 // Helper for |InferLabelForElement()| that infers a label, if possible, from |
| 190 // a previous sibling of |element|, | 192 // a previous sibling of |element|, |
| 191 // e.g. Some Text <input ...> | 193 // e.g. Some Text <input ...> |
| 192 // or Some <span>Text</span> <input ...> | 194 // or Some <span>Text</span> <input ...> |
| 193 // or <p>Some Text</p><input ...> | 195 // or <p>Some Text</p><input ...> |
| 194 // or <label>Some Text</label> <input ...> | 196 // or <label>Some Text</label> <input ...> |
| 195 // or Some Text <img><input ...> | 197 // or Some Text <img><input ...> |
| 196 // or <b>Some Text</b><br/> <input ...>. | 198 // or <b>Some Text</b><br/> <input ...>. |
| 197 string16 InferLabelFromPrevious(const WebFormControlElement& element) { | 199 base::string16 InferLabelFromPrevious(const WebFormControlElement& element) { |
| 198 string16 inferred_label; | 200 base::string16 inferred_label; |
| 199 WebNode previous = element; | 201 WebNode previous = element; |
| 200 while (true) { | 202 while (true) { |
| 201 previous = previous.previousSibling(); | 203 previous = previous.previousSibling(); |
| 202 if (previous.isNull()) | 204 if (previous.isNull()) |
| 203 break; | 205 break; |
| 204 | 206 |
| 205 // Skip over comments. | 207 // Skip over comments. |
| 206 WebNode::NodeType node_type = previous.nodeType(); | 208 WebNode::NodeType node_type = previous.nodeType(); |
| 207 if (node_type == WebNode::CommentNode) | 209 if (node_type == WebNode::CommentNode) |
| 208 continue; | 210 continue; |
| 209 | 211 |
| 210 // Otherwise, only consider normal HTML elements and their contents. | 212 // Otherwise, only consider normal HTML elements and their contents. |
| 211 if (node_type != WebNode::TextNode && | 213 if (node_type != WebNode::TextNode && |
| 212 node_type != WebNode::ElementNode) | 214 node_type != WebNode::ElementNode) |
| 213 break; | 215 break; |
| 214 | 216 |
| 215 // A label might be split across multiple "lightweight" nodes. | 217 // A label might be split across multiple "lightweight" nodes. |
| 216 // Coalesce any text contained in multiple consecutive | 218 // Coalesce any text contained in multiple consecutive |
| 217 // (a) plain text nodes or | 219 // (a) plain text nodes or |
| 218 // (b) inline HTML elements that are essentially equivalent to text nodes. | 220 // (b) inline HTML elements that are essentially equivalent to text nodes. |
| 219 CR_DEFINE_STATIC_LOCAL(WebString, kBold, ("b")); | 221 CR_DEFINE_STATIC_LOCAL(WebString, kBold, ("b")); |
| 220 CR_DEFINE_STATIC_LOCAL(WebString, kStrong, ("strong")); | 222 CR_DEFINE_STATIC_LOCAL(WebString, kStrong, ("strong")); |
| 221 CR_DEFINE_STATIC_LOCAL(WebString, kSpan, ("span")); | 223 CR_DEFINE_STATIC_LOCAL(WebString, kSpan, ("span")); |
| 222 CR_DEFINE_STATIC_LOCAL(WebString, kFont, ("font")); | 224 CR_DEFINE_STATIC_LOCAL(WebString, kFont, ("font")); |
| 223 if (previous.isTextNode() || | 225 if (previous.isTextNode() || |
| 224 HasTagName(previous, kBold) || HasTagName(previous, kStrong) || | 226 HasTagName(previous, kBold) || HasTagName(previous, kStrong) || |
| 225 HasTagName(previous, kSpan) || HasTagName(previous, kFont)) { | 227 HasTagName(previous, kSpan) || HasTagName(previous, kFont)) { |
| 226 string16 value = FindChildText(previous); | 228 base::string16 value = FindChildText(previous); |
| 227 // A text node's value will be empty if it is for a line break. | 229 // A text node's value will be empty if it is for a line break. |
| 228 bool add_space = previous.isTextNode() && value.empty(); | 230 bool add_space = previous.isTextNode() && value.empty(); |
| 229 inferred_label = | 231 inferred_label = |
| 230 CombineAndCollapseWhitespace(value, inferred_label, add_space); | 232 CombineAndCollapseWhitespace(value, inferred_label, add_space); |
| 231 continue; | 233 continue; |
| 232 } | 234 } |
| 233 | 235 |
| 234 // If we have identified a partial label and have reached a non-lightweight | 236 // If we have identified a partial label and have reached a non-lightweight |
| 235 // element, consider the label to be complete. | 237 // element, consider the label to be complete. |
| 236 string16 trimmed_label; | 238 base::string16 trimmed_label; |
| 237 TrimWhitespace(inferred_label, TRIM_ALL, &trimmed_label); | 239 TrimWhitespace(inferred_label, TRIM_ALL, &trimmed_label); |
| 238 if (!trimmed_label.empty()) | 240 if (!trimmed_label.empty()) |
| 239 break; | 241 break; |
| 240 | 242 |
| 241 // <img> and <br> tags often appear between the input element and its | 243 // <img> and <br> tags often appear between the input element and its |
| 242 // label text, so skip over them. | 244 // label text, so skip over them. |
| 243 CR_DEFINE_STATIC_LOCAL(WebString, kImage, ("img")); | 245 CR_DEFINE_STATIC_LOCAL(WebString, kImage, ("img")); |
| 244 CR_DEFINE_STATIC_LOCAL(WebString, kBreak, ("br")); | 246 CR_DEFINE_STATIC_LOCAL(WebString, kBreak, ("br")); |
| 245 if (HasTagName(previous, kImage) || HasTagName(previous, kBreak)) | 247 if (HasTagName(previous, kImage) || HasTagName(previous, kBreak)) |
| 246 continue; | 248 continue; |
| 247 | 249 |
| 248 // We only expect <p> and <label> tags to contain the full label text. | 250 // We only expect <p> and <label> tags to contain the full label text. |
| 249 CR_DEFINE_STATIC_LOCAL(WebString, kPage, ("p")); | 251 CR_DEFINE_STATIC_LOCAL(WebString, kPage, ("p")); |
| 250 CR_DEFINE_STATIC_LOCAL(WebString, kLabel, ("label")); | 252 CR_DEFINE_STATIC_LOCAL(WebString, kLabel, ("label")); |
| 251 if (HasTagName(previous, kPage) || HasTagName(previous, kLabel)) | 253 if (HasTagName(previous, kPage) || HasTagName(previous, kLabel)) |
| 252 inferred_label = FindChildText(previous); | 254 inferred_label = FindChildText(previous); |
| 253 | 255 |
| 254 break; | 256 break; |
| 255 } | 257 } |
| 256 | 258 |
| 257 TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label); | 259 TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label); |
| 258 return inferred_label; | 260 return inferred_label; |
| 259 } | 261 } |
| 260 | 262 |
| 261 // Helper for |InferLabelForElement()| that infers a label, if possible, from | 263 // Helper for |InferLabelForElement()| that infers a label, if possible, from |
| 262 // enclosing list item, | 264 // enclosing list item, |
| 263 // e.g. <li>Some Text<input ...><input ...><input ...></tr> | 265 // e.g. <li>Some Text<input ...><input ...><input ...></tr> |
| 264 string16 InferLabelFromListItem(const WebFormControlElement& element) { | 266 base::string16 InferLabelFromListItem(const WebFormControlElement& element) { |
| 265 WebNode parent = element.parentNode(); | 267 WebNode parent = element.parentNode(); |
| 266 CR_DEFINE_STATIC_LOCAL(WebString, kListItem, ("li")); | 268 CR_DEFINE_STATIC_LOCAL(WebString, kListItem, ("li")); |
| 267 while (!parent.isNull() && parent.isElementNode() && | 269 while (!parent.isNull() && parent.isElementNode() && |
| 268 !parent.to<WebElement>().hasTagName(kListItem)) { | 270 !parent.to<WebElement>().hasTagName(kListItem)) { |
| 269 parent = parent.parentNode(); | 271 parent = parent.parentNode(); |
| 270 } | 272 } |
| 271 | 273 |
| 272 if (!parent.isNull() && HasTagName(parent, kListItem)) | 274 if (!parent.isNull() && HasTagName(parent, kListItem)) |
| 273 return FindChildText(parent); | 275 return FindChildText(parent); |
| 274 | 276 |
| 275 return string16(); | 277 return base::string16(); |
| 276 } | 278 } |
| 277 | 279 |
| 278 // Helper for |InferLabelForElement()| that infers a label, if possible, from | 280 // Helper for |InferLabelForElement()| that infers a label, if possible, from |
| 279 // surrounding table structure, | 281 // surrounding table structure, |
| 280 // e.g. <tr><td>Some Text</td><td><input ...></td></tr> | 282 // e.g. <tr><td>Some Text</td><td><input ...></td></tr> |
| 281 // or <tr><th>Some Text</th><td><input ...></td></tr> | 283 // or <tr><th>Some Text</th><td><input ...></td></tr> |
| 282 // or <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr> | 284 // or <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr> |
| 283 // or <tr><th><b>Some Text</b></th><td><b><input ...></b></td></tr> | 285 // or <tr><th><b>Some Text</b></th><td><b><input ...></b></td></tr> |
| 284 string16 InferLabelFromTableColumn(const WebFormControlElement& element) { | 286 base::string16 InferLabelFromTableColumn(const WebFormControlElement& element) { |
| 285 CR_DEFINE_STATIC_LOCAL(WebString, kTableCell, ("td")); | 287 CR_DEFINE_STATIC_LOCAL(WebString, kTableCell, ("td")); |
| 286 WebNode parent = element.parentNode(); | 288 WebNode parent = element.parentNode(); |
| 287 while (!parent.isNull() && parent.isElementNode() && | 289 while (!parent.isNull() && parent.isElementNode() && |
| 288 !parent.to<WebElement>().hasTagName(kTableCell)) { | 290 !parent.to<WebElement>().hasTagName(kTableCell)) { |
| 289 parent = parent.parentNode(); | 291 parent = parent.parentNode(); |
| 290 } | 292 } |
| 291 | 293 |
| 292 if (parent.isNull()) | 294 if (parent.isNull()) |
| 293 return string16(); | 295 return base::string16(); |
| 294 | 296 |
| 295 // Check all previous siblings, skipping non-element nodes, until we find a | 297 // Check all previous siblings, skipping non-element nodes, until we find a |
| 296 // non-empty text block. | 298 // non-empty text block. |
| 297 string16 inferred_label; | 299 base::string16 inferred_label; |
| 298 WebNode previous = parent.previousSibling(); | 300 WebNode previous = parent.previousSibling(); |
| 299 CR_DEFINE_STATIC_LOCAL(WebString, kTableHeader, ("th")); | 301 CR_DEFINE_STATIC_LOCAL(WebString, kTableHeader, ("th")); |
| 300 while (inferred_label.empty() && !previous.isNull()) { | 302 while (inferred_label.empty() && !previous.isNull()) { |
| 301 if (HasTagName(previous, kTableCell) || HasTagName(previous, kTableHeader)) | 303 if (HasTagName(previous, kTableCell) || HasTagName(previous, kTableHeader)) |
| 302 inferred_label = FindChildText(previous); | 304 inferred_label = FindChildText(previous); |
| 303 | 305 |
| 304 previous = previous.previousSibling(); | 306 previous = previous.previousSibling(); |
| 305 } | 307 } |
| 306 | 308 |
| 307 return inferred_label; | 309 return inferred_label; |
| 308 } | 310 } |
| 309 | 311 |
| 310 // Helper for |InferLabelForElement()| that infers a label, if possible, from | 312 // Helper for |InferLabelForElement()| that infers a label, if possible, from |
| 311 // surrounding table structure, | 313 // surrounding table structure, |
| 312 // e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr> | 314 // e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr> |
| 313 string16 InferLabelFromTableRow(const WebFormControlElement& element) { | 315 base::string16 InferLabelFromTableRow(const WebFormControlElement& element) { |
| 314 CR_DEFINE_STATIC_LOCAL(WebString, kTableRow, ("tr")); | 316 CR_DEFINE_STATIC_LOCAL(WebString, kTableRow, ("tr")); |
| 315 WebNode parent = element.parentNode(); | 317 WebNode parent = element.parentNode(); |
| 316 while (!parent.isNull() && parent.isElementNode() && | 318 while (!parent.isNull() && parent.isElementNode() && |
| 317 !parent.to<WebElement>().hasTagName(kTableRow)) { | 319 !parent.to<WebElement>().hasTagName(kTableRow)) { |
| 318 parent = parent.parentNode(); | 320 parent = parent.parentNode(); |
| 319 } | 321 } |
| 320 | 322 |
| 321 if (parent.isNull()) | 323 if (parent.isNull()) |
| 322 return string16(); | 324 return base::string16(); |
| 323 | 325 |
| 324 // Check all previous siblings, skipping non-element nodes, until we find a | 326 // Check all previous siblings, skipping non-element nodes, until we find a |
| 325 // non-empty text block. | 327 // non-empty text block. |
| 326 string16 inferred_label; | 328 base::string16 inferred_label; |
| 327 WebNode previous = parent.previousSibling(); | 329 WebNode previous = parent.previousSibling(); |
| 328 while (inferred_label.empty() && !previous.isNull()) { | 330 while (inferred_label.empty() && !previous.isNull()) { |
| 329 if (HasTagName(previous, kTableRow)) | 331 if (HasTagName(previous, kTableRow)) |
| 330 inferred_label = FindChildText(previous); | 332 inferred_label = FindChildText(previous); |
| 331 | 333 |
| 332 previous = previous.previousSibling(); | 334 previous = previous.previousSibling(); |
| 333 } | 335 } |
| 334 | 336 |
| 335 return inferred_label; | 337 return inferred_label; |
| 336 } | 338 } |
| 337 | 339 |
| 338 // Helper for |InferLabelForElement()| that infers a label, if possible, from | 340 // Helper for |InferLabelForElement()| that infers a label, if possible, from |
| 339 // a surrounding div table, | 341 // a surrounding div table, |
| 340 // e.g. <div>Some Text<span><input ...></span></div> | 342 // e.g. <div>Some Text<span><input ...></span></div> |
| 341 // e.g. <div>Some Text</div><div><input ...></div> | 343 // e.g. <div>Some Text</div><div><input ...></div> |
| 342 string16 InferLabelFromDivTable(const WebFormControlElement& element) { | 344 base::string16 InferLabelFromDivTable(const WebFormControlElement& element) { |
| 343 WebNode node = element.parentNode(); | 345 WebNode node = element.parentNode(); |
| 344 bool looking_for_parent = true; | 346 bool looking_for_parent = true; |
| 345 | 347 |
| 346 // Search the sibling and parent <div>s until we find a candidate label. | 348 // Search the sibling and parent <div>s until we find a candidate label. |
| 347 string16 inferred_label; | 349 base::string16 inferred_label; |
| 348 CR_DEFINE_STATIC_LOCAL(WebString, kDiv, ("div")); | 350 CR_DEFINE_STATIC_LOCAL(WebString, kDiv, ("div")); |
| 349 CR_DEFINE_STATIC_LOCAL(WebString, kTable, ("table")); | 351 CR_DEFINE_STATIC_LOCAL(WebString, kTable, ("table")); |
| 350 CR_DEFINE_STATIC_LOCAL(WebString, kFieldSet, ("fieldset")); | 352 CR_DEFINE_STATIC_LOCAL(WebString, kFieldSet, ("fieldset")); |
| 351 while (inferred_label.empty() && !node.isNull()) { | 353 while (inferred_label.empty() && !node.isNull()) { |
| 352 if (HasTagName(node, kDiv)) { | 354 if (HasTagName(node, kDiv)) { |
| 353 looking_for_parent = false; | 355 looking_for_parent = false; |
| 354 inferred_label = FindChildText(node); | 356 inferred_label = FindChildText(node); |
| 355 } else if (looking_for_parent && | 357 } else if (looking_for_parent && |
| 356 (HasTagName(node, kTable) || HasTagName(node, kFieldSet))) { | 358 (HasTagName(node, kTable) || HasTagName(node, kFieldSet))) { |
| 357 // If the element is in a table or fieldset, its label most likely is too. | 359 // If the element is in a table or fieldset, its label most likely is too. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 369 node = node.previousSibling(); | 371 node = node.previousSibling(); |
| 370 } | 372 } |
| 371 | 373 |
| 372 return inferred_label; | 374 return inferred_label; |
| 373 } | 375 } |
| 374 | 376 |
| 375 // Helper for |InferLabelForElement()| that infers a label, if possible, from | 377 // Helper for |InferLabelForElement()| that infers a label, if possible, from |
| 376 // a surrounding definition list, | 378 // a surrounding definition list, |
| 377 // e.g. <dl><dt>Some Text</dt><dd><input ...></dd></dl> | 379 // e.g. <dl><dt>Some Text</dt><dd><input ...></dd></dl> |
| 378 // e.g. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl> | 380 // e.g. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl> |
| 379 string16 InferLabelFromDefinitionList(const WebFormControlElement& element) { | 381 base::string16 InferLabelFromDefinitionList( |
| 382 const WebFormControlElement& element) { |
| 380 CR_DEFINE_STATIC_LOCAL(WebString, kDefinitionData, ("dd")); | 383 CR_DEFINE_STATIC_LOCAL(WebString, kDefinitionData, ("dd")); |
| 381 WebNode parent = element.parentNode(); | 384 WebNode parent = element.parentNode(); |
| 382 while (!parent.isNull() && parent.isElementNode() && | 385 while (!parent.isNull() && parent.isElementNode() && |
| 383 !parent.to<WebElement>().hasTagName(kDefinitionData)) | 386 !parent.to<WebElement>().hasTagName(kDefinitionData)) |
| 384 parent = parent.parentNode(); | 387 parent = parent.parentNode(); |
| 385 | 388 |
| 386 if (parent.isNull() || !HasTagName(parent, kDefinitionData)) | 389 if (parent.isNull() || !HasTagName(parent, kDefinitionData)) |
| 387 return string16(); | 390 return base::string16(); |
| 388 | 391 |
| 389 // Skip by any intervening text nodes. | 392 // Skip by any intervening text nodes. |
| 390 WebNode previous = parent.previousSibling(); | 393 WebNode previous = parent.previousSibling(); |
| 391 while (!previous.isNull() && previous.isTextNode()) | 394 while (!previous.isNull() && previous.isTextNode()) |
| 392 previous = previous.previousSibling(); | 395 previous = previous.previousSibling(); |
| 393 | 396 |
| 394 CR_DEFINE_STATIC_LOCAL(WebString, kDefinitionTag, ("dt")); | 397 CR_DEFINE_STATIC_LOCAL(WebString, kDefinitionTag, ("dt")); |
| 395 if (previous.isNull() || !HasTagName(previous, kDefinitionTag)) | 398 if (previous.isNull() || !HasTagName(previous, kDefinitionTag)) |
| 396 return string16(); | 399 return base::string16(); |
| 397 | 400 |
| 398 return FindChildText(previous); | 401 return FindChildText(previous); |
| 399 } | 402 } |
| 400 | 403 |
| 401 // Infers corresponding label for |element| from surrounding context in the DOM, | 404 // Infers corresponding label for |element| from surrounding context in the DOM, |
| 402 // e.g. the contents of the preceding <p> tag or text element. | 405 // e.g. the contents of the preceding <p> tag or text element. |
| 403 string16 InferLabelForElement(const WebFormControlElement& element) { | 406 base::string16 InferLabelForElement(const WebFormControlElement& element) { |
| 404 string16 inferred_label = InferLabelFromPrevious(element); | 407 base::string16 inferred_label = InferLabelFromPrevious(element); |
| 405 if (!inferred_label.empty()) | 408 if (!inferred_label.empty()) |
| 406 return inferred_label; | 409 return inferred_label; |
| 407 | 410 |
| 408 // If we didn't find a label, check for list item case. | 411 // If we didn't find a label, check for list item case. |
| 409 inferred_label = InferLabelFromListItem(element); | 412 inferred_label = InferLabelFromListItem(element); |
| 410 if (!inferred_label.empty()) | 413 if (!inferred_label.empty()) |
| 411 return inferred_label; | 414 return inferred_label; |
| 412 | 415 |
| 413 // If we didn't find a label, check for table cell case. | 416 // If we didn't find a label, check for table cell case. |
| 414 inferred_label = InferLabelFromTableColumn(element); | 417 inferred_label = InferLabelFromTableColumn(element); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 425 if (!inferred_label.empty()) | 428 if (!inferred_label.empty()) |
| 426 return inferred_label; | 429 return inferred_label; |
| 427 | 430 |
| 428 // If we didn't find a label, check for div table case. | 431 // If we didn't find a label, check for div table case. |
| 429 return InferLabelFromDivTable(element); | 432 return InferLabelFromDivTable(element); |
| 430 } | 433 } |
| 431 | 434 |
| 432 // Fills |option_strings| with the values of the <option> elements present in | 435 // Fills |option_strings| with the values of the <option> elements present in |
| 433 // |select_element|. | 436 // |select_element|. |
| 434 void GetOptionStringsFromElement(const WebSelectElement& select_element, | 437 void GetOptionStringsFromElement(const WebSelectElement& select_element, |
| 435 std::vector<string16>* option_values, | 438 std::vector<base::string16>* option_values, |
| 436 std::vector<string16>* option_contents) { | 439 std::vector<base::string16>* option_contents) { |
| 437 DCHECK(!select_element.isNull()); | 440 DCHECK(!select_element.isNull()); |
| 438 | 441 |
| 439 option_values->clear(); | 442 option_values->clear(); |
| 440 option_contents->clear(); | 443 option_contents->clear(); |
| 441 WebVector<WebElement> list_items = select_element.listItems(); | 444 WebVector<WebElement> list_items = select_element.listItems(); |
| 442 option_values->reserve(list_items.size()); | 445 option_values->reserve(list_items.size()); |
| 443 option_contents->reserve(list_items.size()); | 446 option_contents->reserve(list_items.size()); |
| 444 for (size_t i = 0; i < list_items.size(); ++i) { | 447 for (size_t i = 0; i < list_items.size(); ++i) { |
| 445 if (IsOptionElement(list_items[i])) { | 448 if (IsOptionElement(list_items[i])) { |
| 446 const WebOptionElement option = list_items[i].toConst<WebOptionElement>(); | 449 const WebOptionElement option = list_items[i].toConst<WebOptionElement>(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 475 } | 478 } |
| 476 | 479 |
| 477 // It's possible that the site has injected fields into the form after the | 480 // It's possible that the site has injected fields into the form after the |
| 478 // page has loaded, so we can't assert that the size of the cached control | 481 // page has loaded, so we can't assert that the size of the cached control |
| 479 // elements is equal to the size of the fields in |form|. Fortunately, the | 482 // elements is equal to the size of the fields in |form|. Fortunately, the |
| 480 // one case in the wild where this happens, paypal.com signup form, the fields | 483 // one case in the wild where this happens, paypal.com signup form, the fields |
| 481 // are appended to the end of the form and are not visible. | 484 // are appended to the end of the form and are not visible. |
| 482 for (size_t i = 0; i < control_elements.size(); ++i) { | 485 for (size_t i = 0; i < control_elements.size(); ++i) { |
| 483 WebFormControlElement* element = &control_elements[i]; | 486 WebFormControlElement* element = &control_elements[i]; |
| 484 | 487 |
| 485 if (string16(element->nameForAutofill()) != data.fields[i].name) { | 488 if (base::string16(element->nameForAutofill()) != data.fields[i].name) { |
| 486 // This case should be reachable only for pathological websites, which | 489 // This case should be reachable only for pathological websites, which |
| 487 // rename form fields while the user is interacting with the Autofill | 490 // rename form fields while the user is interacting with the Autofill |
| 488 // popup. I (isherman) am not aware of any such websites, and so am | 491 // popup. I (isherman) am not aware of any such websites, and so am |
| 489 // optimistically including a NOTREACHED(). If you ever trip this check, | 492 // optimistically including a NOTREACHED(). If you ever trip this check, |
| 490 // please file a bug against me. | 493 // please file a bug against me. |
| 491 NOTREACHED(); | 494 NOTREACHED(); |
| 492 continue; | 495 continue; |
| 493 } | 496 } |
| 494 | 497 |
| 495 bool is_initiating_element = (*element == initiating_element); | 498 bool is_initiating_element = (*element == initiating_element); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 605 if (!element) | 608 if (!element) |
| 606 return false; | 609 return false; |
| 607 | 610 |
| 608 return element->isCheckbox() || element->isRadioButton(); | 611 return element->isCheckbox() || element->isRadioButton(); |
| 609 } | 612 } |
| 610 | 613 |
| 611 bool IsAutofillableInputElement(const WebInputElement* element) { | 614 bool IsAutofillableInputElement(const WebInputElement* element) { |
| 612 return IsTextInput(element) || IsCheckableElement(element); | 615 return IsTextInput(element) || IsCheckableElement(element); |
| 613 } | 616 } |
| 614 | 617 |
| 615 const string16 GetFormIdentifier(const WebFormElement& form) { | 618 const base::string16 GetFormIdentifier(const WebFormElement& form) { |
| 616 string16 identifier = form.name(); | 619 base::string16 identifier = form.name(); |
| 617 CR_DEFINE_STATIC_LOCAL(WebString, kId, ("id")); | 620 CR_DEFINE_STATIC_LOCAL(WebString, kId, ("id")); |
| 618 if (identifier.empty()) | 621 if (identifier.empty()) |
| 619 identifier = form.getAttribute(kId); | 622 identifier = form.getAttribute(kId); |
| 620 | 623 |
| 621 return identifier; | 624 return identifier; |
| 622 } | 625 } |
| 623 | 626 |
| 624 bool ClickElement(const WebDocument& document, | 627 bool ClickElement(const WebDocument& document, |
| 625 const WebElementDescriptor& element_descriptor) { | 628 const WebElementDescriptor& element_descriptor) { |
| 626 WebString web_descriptor = WebString::fromUTF8(element_descriptor.descriptor); | 629 WebString web_descriptor = WebString::fromUTF8(element_descriptor.descriptor); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 720 DCHECK(IsSelectElement(element)); | 723 DCHECK(IsSelectElement(element)); |
| 721 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 724 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
| 722 GetOptionStringsFromElement(select_element, | 725 GetOptionStringsFromElement(select_element, |
| 723 &field->option_values, | 726 &field->option_values, |
| 724 &field->option_contents); | 727 &field->option_contents); |
| 725 } | 728 } |
| 726 | 729 |
| 727 if (!(extract_mask & EXTRACT_VALUE)) | 730 if (!(extract_mask & EXTRACT_VALUE)) |
| 728 return; | 731 return; |
| 729 | 732 |
| 730 string16 value; | 733 base::string16 value; |
| 731 if (IsAutofillableInputElement(input_element)) { | 734 if (IsAutofillableInputElement(input_element)) { |
| 732 value = input_element->value(); | 735 value = input_element->value(); |
| 733 } else { | 736 } else { |
| 734 DCHECK(IsSelectElement(element)); | 737 DCHECK(IsSelectElement(element)); |
| 735 const WebSelectElement select_element = element.toConst<WebSelectElement>(); | 738 const WebSelectElement select_element = element.toConst<WebSelectElement>(); |
| 736 value = select_element.value(); | 739 value = select_element.value(); |
| 737 | 740 |
| 738 // Convert the |select_element| value to text if requested. | 741 // Convert the |select_element| value to text if requested. |
| 739 if (extract_mask & EXTRACT_OPTION_TEXT) { | 742 if (extract_mask & EXTRACT_OPTION_TEXT) { |
| 740 WebVector<WebElement> list_items = select_element.listItems(); | 743 WebVector<WebElement> list_items = select_element.listItems(); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 782 form->origin = frame->document().url(); | 785 form->origin = frame->document().url(); |
| 783 form->action = frame->document().completeURL(form_element.action()); | 786 form->action = frame->document().completeURL(form_element.action()); |
| 784 form->user_submitted = form_element.wasUserSubmitted(); | 787 form->user_submitted = form_element.wasUserSubmitted(); |
| 785 | 788 |
| 786 // If the completed URL is not valid, just use the action we get from | 789 // If the completed URL is not valid, just use the action we get from |
| 787 // WebKit. | 790 // WebKit. |
| 788 if (!form->action.is_valid()) | 791 if (!form->action.is_valid()) |
| 789 form->action = GURL(form_element.action()); | 792 form->action = GURL(form_element.action()); |
| 790 | 793 |
| 791 // A map from a FormFieldData's name to the FormFieldData itself. | 794 // A map from a FormFieldData's name to the FormFieldData itself. |
| 792 std::map<string16, FormFieldData*> name_map; | 795 std::map<base::string16, FormFieldData*> name_map; |
| 793 | 796 |
| 794 // The extracted FormFields. We use pointers so we can store them in | 797 // The extracted FormFields. We use pointers so we can store them in |
| 795 // |name_map|. | 798 // |name_map|. |
| 796 ScopedVector<FormFieldData> form_fields; | 799 ScopedVector<FormFieldData> form_fields; |
| 797 | 800 |
| 798 WebVector<WebFormControlElement> control_elements; | 801 WebVector<WebFormControlElement> control_elements; |
| 799 form_element.getFormControlElements(control_elements); | 802 form_element.getFormControlElements(control_elements); |
| 800 | 803 |
| 801 // A vector of bools that indicate whether each field in the form meets the | 804 // A vector of bools that indicate whether each field in the form meets the |
| 802 // requirements and thus will be in the resulting |form|. | 805 // requirements and thus will be in the resulting |form|. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 834 // element, get the corresponding form control element, use the form control | 837 // element, get the corresponding form control element, use the form control |
| 835 // element's name as a key into the <name, FormFieldData> map to find the | 838 // element's name as a key into the <name, FormFieldData> map to find the |
| 836 // previously created FormFieldData and set the FormFieldData's label to the | 839 // previously created FormFieldData and set the FormFieldData's label to the |
| 837 // label.firstChild().nodeValue() of the label element. | 840 // label.firstChild().nodeValue() of the label element. |
| 838 WebNodeList labels = form_element.getElementsByTagName(kLabel); | 841 WebNodeList labels = form_element.getElementsByTagName(kLabel); |
| 839 for (unsigned i = 0; i < labels.length(); ++i) { | 842 for (unsigned i = 0; i < labels.length(); ++i) { |
| 840 WebLabelElement label = labels.item(i).to<WebLabelElement>(); | 843 WebLabelElement label = labels.item(i).to<WebLabelElement>(); |
| 841 WebFormControlElement field_element = | 844 WebFormControlElement field_element = |
| 842 label.correspondingControl().to<WebFormControlElement>(); | 845 label.correspondingControl().to<WebFormControlElement>(); |
| 843 | 846 |
| 844 string16 element_name; | 847 base::string16 element_name; |
| 845 if (field_element.isNull()) { | 848 if (field_element.isNull()) { |
| 846 // Sometimes site authors will incorrectly specify the corresponding | 849 // Sometimes site authors will incorrectly specify the corresponding |
| 847 // field element's name rather than its id, so we compensate here. | 850 // field element's name rather than its id, so we compensate here. |
| 848 element_name = label.getAttribute(kFor); | 851 element_name = label.getAttribute(kFor); |
| 849 } else if ( | 852 } else if ( |
| 850 !field_element.isFormControlElement() || | 853 !field_element.isFormControlElement() || |
| 851 field_element.formControlType() == kHidden) { | 854 field_element.formControlType() == kHidden) { |
| 852 continue; | 855 continue; |
| 853 } else { | 856 } else { |
| 854 element_name = field_element.nameForAutofill(); | 857 element_name = field_element.nameForAutofill(); |
| 855 } | 858 } |
| 856 | 859 |
| 857 std::map<string16, FormFieldData*>::iterator iter = | 860 std::map<base::string16, FormFieldData*>::iterator iter = |
| 858 name_map.find(element_name); | 861 name_map.find(element_name); |
| 859 if (iter != name_map.end()) { | 862 if (iter != name_map.end()) { |
| 860 string16 label_text = FindChildText(label); | 863 base::string16 label_text = FindChildText(label); |
| 861 | 864 |
| 862 // Concatenate labels because some sites might have multiple label | 865 // Concatenate labels because some sites might have multiple label |
| 863 // candidates. | 866 // candidates. |
| 864 if (!iter->second->label.empty() && !label_text.empty()) | 867 if (!iter->second->label.empty() && !label_text.empty()) |
| 865 iter->second->label += ASCIIToUTF16(" "); | 868 iter->second->label += ASCIIToUTF16(" "); |
| 866 iter->second->label += label_text; | 869 iter->second->label += label_text; |
| 867 } | 870 } |
| 868 } | 871 } |
| 869 | 872 |
| 870 // Loop through the form control elements, extracting the label text from | 873 // Loop through the form control elements, extracting the label text from |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1019 continue; | 1022 continue; |
| 1020 | 1023 |
| 1021 if (input_element->isAutofilled()) | 1024 if (input_element->isAutofilled()) |
| 1022 return true; | 1025 return true; |
| 1023 } | 1026 } |
| 1024 | 1027 |
| 1025 return false; | 1028 return false; |
| 1026 } | 1029 } |
| 1027 | 1030 |
| 1028 } // namespace autofill | 1031 } // namespace autofill |
| OLD | NEW |