1 /* 2 * Copyright 2006, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "CachedPrefix.h" 27 #include "CachedNode.h" 28 #include "CachedRoot.h" 29 #include "Document.h" 30 #include "EventListener.h" 31 #include "EventNames.h" 32 #include "Frame.h" 33 #include "FrameLoader.h" 34 #include "FrameLoaderClientAndroid.h" 35 #include "FrameTree.h" 36 #include "FrameView.h" 37 //#include "GraphicsContext.h" 38 #include "GraphicsLayerAndroid.h" 39 #include "HTMLAreaElement.h" 40 #include "HTMLImageElement.h" 41 #include "HTMLInputElement.h" 42 #include "HTMLMapElement.h" 43 #include "HTMLNames.h" 44 #include "HTMLOptionElement.h" 45 #include "HTMLSelectElement.h" 46 #include "HTMLTextAreaElement.h" 47 #include "InlineTextBox.h" 48 #include "KURL.h" 49 #include "PluginView.h" 50 #include "RegisteredEventListener.h" 51 #include "RenderImage.h" 52 #include "RenderInline.h" 53 #include "RenderLayerBacking.h" 54 #include "RenderListBox.h" 55 #include "RenderSkinCombo.h" 56 #include "RenderTextControl.h" 57 #include "RenderWidget.h" 58 #include "SkCanvas.h" 59 #include "SkPoint.h" 60 #include "Text.h" 61 #include "WebCoreFrameBridge.h" 62 #include "WebCoreViewBridge.h" 63 #include "Widget.h" 64 #include <wtf/unicode/Unicode.h> 65 66 #ifdef DUMP_NAV_CACHE_USING_PRINTF 67 FILE* gNavCacheLogFile = NULL; 68 android::Mutex gWriteLogMutex; 69 #endif 70 71 #include "CacheBuilder.h" 72 73 #define MINIMUM_FOCUSABLE_WIDTH 3 74 #define MINIMUM_FOCUSABLE_HEIGHT 3 75 #define MAXIMUM_FOCUS_RING_COUNT 32 76 77 namespace android { 78 79 CacheBuilder* CacheBuilder::Builder(Frame* frame) { 80 return &((FrameLoaderClientAndroid*) frame->loader()->client())->getCacheBuilder(); 81 } 82 83 Frame* CacheBuilder::FrameAnd(CacheBuilder* cacheBuilder) { 84 FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*) 85 ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder)); 86 return loader->getFrame(); 87 } 88 89 Frame* CacheBuilder::FrameAnd(const CacheBuilder* cacheBuilder) { 90 FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*) 91 ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder)); 92 return loader->getFrame(); 93 } 94 95 96 #if DUMP_NAV_CACHE 97 98 static bool hasEventListener(Node* node, const AtomicString& eventType) { 99 if (!node->isElementNode()) 100 return false; 101 Element* element = static_cast<Element*>(node); 102 EventListener* listener = element->getAttributeEventListener(eventType); 103 return 0 != listener; 104 } 105 106 #define DEBUG_BUFFER_SIZE 256 107 #define DEBUG_WRAP_SIZE 150 108 #define DEBUG_WRAP_MAX 170 109 110 Frame* CacheBuilder::Debug::frameAnd() const { 111 CacheBuilder* nav = (CacheBuilder*) ((char*) this - OFFSETOF(CacheBuilder, mDebug)); 112 return CacheBuilder::FrameAnd(nav); 113 } 114 115 void CacheBuilder::Debug::attr(const AtomicString& name, const AtomicString& value) { 116 if (name.isNull() || name.isEmpty() || value.isNull() || value.isEmpty()) 117 return; 118 uChar(name.characters(), name.length(), false); 119 print("="); 120 wideString(value.characters(), value.length(), false); 121 print(" "); 122 } 123 124 void CacheBuilder::Debug::comma(const char* str) { 125 print(str); 126 print(", "); 127 } 128 129 void CacheBuilder::Debug::flush() { 130 int len; 131 do { 132 int limit = mIndex; 133 if (limit < DEBUG_WRAP_SIZE) 134 return; 135 if (limit < DEBUG_WRAP_MAX) 136 len = limit; 137 else { 138 limit = DEBUG_WRAP_MAX; 139 len = DEBUG_WRAP_SIZE; 140 while (len < limit) { 141 char test = mBuffer[len]; 142 if (test < '/' || (test > '9' && test < 'A') || (test > 'Z' && test < 'a') || test > 'z') 143 break; 144 len++; 145 } 146 while (len > 0 && mBuffer[len - 1] == '\\') 147 len--; 148 while (mBuffer[len] == '"') 149 len++; 150 } 151 const char* prefix = mPrefix; 152 if (prefix[0] == '\"') { 153 // see if we're inside a quote 154 int quoteCount = 0; 155 for (int index = 0; index < len; index++) { 156 if (mBuffer[index] == '\\') { 157 index++; 158 continue; 159 } 160 if (mBuffer[index] == '\n') { 161 quoteCount = 0; 162 continue; 163 } 164 if (mBuffer[index] == '"') 165 quoteCount++; 166 } 167 if ((quoteCount & 1) == 0) 168 prefix = "\n"; 169 } 170 DUMP_NAV_LOGD("%.*s", len, mBuffer); 171 int copy = mIndex - len; 172 strcpy(mBuffer, prefix); 173 memcpy(&mBuffer[strlen(prefix)], &mBuffer[len], copy); 174 mIndex = strlen(prefix) + copy; 175 } while (true); 176 } 177 178 void CacheBuilder::Debug::frameName(char*& namePtr, const char* max) const { 179 if (namePtr >= max) 180 return; 181 Frame* frame = frameAnd(); 182 Frame* parent = frame->tree()->parent(); 183 if (parent) 184 Builder(parent)->mDebug.frameName(namePtr, max); 185 const AtomicString& name = frame->tree()->name(); 186 if (name.length() == 0) 187 return; 188 unsigned index = 0; 189 if (name.startsWith(AtomicString("opener"))) 190 index = 6; 191 for (; index < name.length(); index++) { 192 char ch = name[index]; 193 if (ch <= ' ') 194 ch = '_'; 195 if (WTF::isASCIIAlphanumeric(ch) || ch == '_') 196 *namePtr++ = ch; 197 } 198 } 199 200 void CacheBuilder::Debug::frames() { 201 Frame* frame = frameAnd(); 202 Document* doc = frame->document(); 203 if (doc == NULL) 204 return; 205 bool top = frame->tree()->parent() == NULL; 206 if (top) { 207 #ifdef DUMP_NAV_CACHE_USING_PRINTF 208 gWriteLogMutex.lock(); 209 ASSERT(gNavCacheLogFile == NULL); 210 gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a"); 211 #endif 212 groups(); 213 } 214 Frame* child = frame->tree()->firstChild(); 215 bool hasChild = child != NULL; 216 if (top && hasChild) 217 DUMP_NAV_LOGD("\nnamespace TEST_SPACE {\n\n"); 218 while (child) { 219 Builder(child)->mDebug.frames(); 220 child = child->tree()->nextSibling(); 221 } 222 if (hasChild) { 223 child = frame->tree()->firstChild(); 224 while (child) { 225 char childName[256]; 226 char* childNamePtr = childName; 227 Builder(child)->mDebug.frameName(childNamePtr, childNamePtr + sizeof(childName) - 1); 228 *childNamePtr = '\0'; 229 if (child == frame->tree()->firstChild()) 230 DUMP_NAV_LOGD("DebugTestFrameGroup TEST%s_GROUP[] = {\n", childName); 231 Frame* next = child->tree()->nextSibling(); 232 Document* doc = child->document(); 233 if (doc != NULL) { 234 RenderObject* renderer = doc->renderer(); 235 if (renderer != NULL) { 236 RenderLayer* layer = renderer->enclosingLayer(); 237 if (layer != NULL) { 238 DUMP_NAV_LOGD("{ "); 239 DUMP_NAV_LOGD("TEST%s_RECTS, ", childName); 240 DUMP_NAV_LOGD("TEST%s_RECT_COUNT, ", childName); 241 DUMP_NAV_LOGD("TEST%s_RECTPARTS, ", childName); 242 DUMP_NAV_LOGD("TEST%s_BOUNDS,\n", childName); 243 DUMP_NAV_LOGD("TEST%s_WIDTH, ", childName); 244 DUMP_NAV_LOGD("TEST%s_HEIGHT,\n", childName); 245 DUMP_NAV_LOGD("0, 0, 0, 0,\n"); 246 DUMP_NAV_LOGD("TEST%s_FOCUS, ", childName); 247 Frame* grandChild = child->tree()->firstChild(); 248 if (grandChild) { 249 char grandChildName[256]; 250 char* grandChildNamePtr = grandChildName; 251 Builder(grandChild)->mDebug.frameName(grandChildNamePtr, 252 grandChildNamePtr + sizeof(grandChildName) - 1); 253 *grandChildNamePtr = '\0'; 254 DUMP_NAV_LOGD("TEST%s_GROUP, ", grandChildName); 255 DUMP_NAV_LOGD("sizeof(TEST%s_GROUP) / sizeof(DebugTestFrameGroup), ", grandChildName); 256 } else 257 DUMP_NAV_LOGD("NULL, 0, "); 258 DUMP_NAV_LOGD("\"%s\"\n", childName); 259 DUMP_NAV_LOGD("}%c\n", next ? ',' : ' '); 260 } 261 } 262 } 263 child = next; 264 } 265 DUMP_NAV_LOGD("};\n"); 266 } 267 if (top) { 268 if (hasChild) 269 DUMP_NAV_LOGD("\n} // end of namespace\n\n"); 270 char name[256]; 271 char* frameNamePtr = name; 272 frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1); 273 *frameNamePtr = '\0'; 274 DUMP_NAV_LOGD("DebugTestFrameGroup TEST%s_GROUP = {\n", name); 275 DUMP_NAV_LOGD("TEST%s_RECTS, ", name); 276 DUMP_NAV_LOGD("TEST%s_RECT_COUNT, ", name); 277 DUMP_NAV_LOGD("TEST%s_RECTPARTS, ", name); 278 DUMP_NAV_LOGD("TEST%s_BOUNDS,\n", name); 279 DUMP_NAV_LOGD("TEST%s_WIDTH, ", name); 280 DUMP_NAV_LOGD("TEST%s_HEIGHT,\n", name); 281 DUMP_NAV_LOGD("TEST%s_MAX_H, ", name); 282 DUMP_NAV_LOGD("TEST%s_MIN_H, ", name); 283 DUMP_NAV_LOGD("TEST%s_MAX_V, ", name); 284 DUMP_NAV_LOGD("TEST%s_MIN_V,\n", name); 285 DUMP_NAV_LOGD("TEST%s_FOCUS, ", name); 286 if (hasChild) { 287 child = frame->tree()->firstChild(); 288 char childName[256]; 289 char* childNamePtr = childName; 290 Builder(child)->mDebug.frameName(childNamePtr, childNamePtr + sizeof(childName) - 1); 291 *childNamePtr = '\0'; 292 DUMP_NAV_LOGD("TEST_SPACE::TEST%s_GROUP, ", childName); 293 DUMP_NAV_LOGD("sizeof(TEST_SPACE::TEST%s_GROUP) / sizeof(DebugTestFrameGroup), \n" ,childName); 294 } else 295 DUMP_NAV_LOGD("NULL, 0, "); 296 DUMP_NAV_LOGD("\"%s\"\n", name); 297 DUMP_NAV_LOGD("};\n"); 298 #ifdef DUMP_NAV_CACHE_USING_PRINTF 299 if (gNavCacheLogFile) 300 fclose(gNavCacheLogFile); 301 gNavCacheLogFile = NULL; 302 gWriteLogMutex.unlock(); 303 #endif 304 } 305 } 306 307 void CacheBuilder::Debug::init(char* buffer, size_t size) { 308 mBuffer = buffer; 309 mBufferSize = size; 310 mIndex = 0; 311 mPrefix = ""; 312 } 313 314 void CacheBuilder::Debug::groups() { 315 Frame* frame = frameAnd(); 316 Frame* child = frame->tree()->firstChild(); 317 bool hasChild = child != NULL; 318 if (frame->tree()->parent() == NULL && hasChild) 319 DUMP_NAV_LOGD("namespace TEST_SPACE {\n\n"); 320 while (child) { 321 Builder(child)->mDebug.groups(); 322 child = child->tree()->nextSibling(); 323 } 324 if (frame->tree()->parent() == NULL && hasChild) 325 DUMP_NAV_LOGD("\n} // end of namespace\n\n"); 326 Document* doc = frame->document(); 327 char name[256]; 328 char* frameNamePtr = name; 329 frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1); 330 *frameNamePtr = '\0'; 331 if (doc == NULL) { 332 DUMP_NAV_LOGD("// %s has no document\n", name); 333 return; 334 } 335 RenderObject* renderer = doc->renderer(); 336 if (renderer == NULL) { 337 DUMP_NAV_LOGD("// %s has no renderer\n", name); 338 return; 339 } 340 RenderLayer* layer = renderer->enclosingLayer(); 341 if (layer == NULL) { 342 DUMP_NAV_LOGD("// %s has no enclosingLayer\n", name); 343 return; 344 } 345 Node* node = doc; 346 Node* focus = doc->focusedNode(); 347 bool atLeastOne = false; 348 do { 349 if ((atLeastOne |= isFocusable(node)) != false) 350 break; 351 } while ((node = node->traverseNextNode()) != NULL); 352 int focusIndex = -1; 353 if (atLeastOne == false) { 354 DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n" 355 "{{0, 0, 0, 0}, \"\", 0, -1, \"\", {0, 0, 0, 0}, false, 0}\n" 356 "};\n\n", name); 357 DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = 1;" 358 " // no focusable nodes\n", name); 359 DUMP_NAV_LOGD("#define TEST%s_RECTPARTS NULL\n", name); 360 } else { 361 node = doc; 362 int count = 1; 363 DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n", name); 364 do { 365 String properties; 366 if (hasEventListener(node, eventNames().clickEvent)) 367 properties.append("ONCLICK | "); 368 if (hasEventListener(node, eventNames().mousedownEvent)) 369 properties.append("MOUSEDOWN | "); 370 if (hasEventListener(node, eventNames().mouseupEvent)) 371 properties.append("MOUSEUP | "); 372 if (hasEventListener(node, eventNames().mouseoverEvent)) 373 properties.append("MOUSEOVER | "); 374 if (hasEventListener(node, eventNames().mouseoutEvent)) 375 properties.append("MOUSEOUT | "); 376 if (hasEventListener(node, eventNames().keydownEvent)) 377 properties.append("KEYDOWN | "); 378 if (hasEventListener(node, eventNames().keyupEvent)) 379 properties.append("KEYUP | "); 380 if (CacheBuilder::HasFrame(node)) 381 properties.append("FRAME | "); 382 if (focus == node) { 383 properties.append("FOCUS | "); 384 focusIndex = count; 385 } 386 if (node->isKeyboardFocusable(NULL)) 387 properties.append("KEYBOARD_FOCUSABLE | "); 388 if (node->isMouseFocusable()) 389 properties.append("MOUSE_FOCUSABLE | "); 390 if (node->isFocusable()) 391 properties.append("SIMPLE_FOCUSABLE | "); 392 if (properties.isEmpty()) 393 properties.append("0"); 394 else 395 properties.truncate(properties.length() - 3); 396 IntRect rect = node->getRect(); 397 if (node->hasTagName(HTMLNames::areaTag)) 398 rect = getAreaRect(static_cast<HTMLAreaElement*>(node)); 399 char buffer[DEBUG_BUFFER_SIZE]; 400 memset(buffer, 0, sizeof(buffer)); 401 mBuffer = buffer; 402 mBufferSize = sizeof(buffer); 403 mPrefix = "\"\n\""; 404 mIndex = snprintf(buffer, sizeof(buffer), "{{%d, %d, %d, %d}, ", rect.x(), rect.y(), 405 rect.width(), rect.height()); 406 localName(node); 407 uChar(properties.characters(), properties.length(), false); 408 print(", "); 409 int parentIndex = ParentIndex(node, count, node->parentNode()); 410 char scratch[256]; 411 snprintf(scratch, sizeof(scratch), "%d", parentIndex); 412 comma(scratch); 413 Element* element = static_cast<Element*>(node); 414 if (node->isElementNode() && element->hasID()) 415 wideString(element->getIDAttribute()); 416 else if (node->isTextNode()) { 417 #if 01 // set to one to abbreviate text that can be omitted from the address detection code 418 if (rect.isEmpty() && node->textContent().length() > 100) { 419 wideString(node->textContent().characters(), 100, false); 420 snprintf(scratch, sizeof(scratch), "/* + %d bytes */", 421 node->textContent().length() - 100); 422 print(scratch); 423 } else 424 #endif 425 wideString(node->textContent().characters(), node->textContent().length(), true); 426 } else if (node->hasTagName(HTMLNames::aTag) || 427 node->hasTagName(HTMLNames::areaTag)) 428 { 429 HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(node); 430 wideString(anchor->href()); 431 } else if (node->hasTagName(HTMLNames::imgTag)) { 432 HTMLImageElement* image = static_cast<HTMLImageElement*>(node); 433 wideString(image->src()); 434 } else 435 print("\"\""); 436 RenderObject* renderer = node->renderer(); 437 int tabindex = node->isElementNode() ? node->tabIndex() : 0; 438 RenderLayer* layer = 0; 439 if (renderer) { 440 const IntRect& absB = renderer->absoluteBoundingBoxRect(); 441 bool hasLayer = renderer->hasLayer(); 442 layer = hasLayer ? toRenderBoxModelObject(renderer)->layer() : 0; 443 snprintf(scratch, sizeof(scratch), ", {%d, %d, %d, %d}, %s" 444 ", %d, %s, %s},", 445 absB.x(), absB.y(), absB.width(), absB.height(), 446 renderer->hasOverflowClip() ? "true" : "false", tabindex, 447 hasLayer ? "true" : "false", 448 hasLayer && layer->isComposited() ? "true" : "false"); 449 // TODO: add renderer->style()->visibility() 450 print(scratch); 451 } else 452 print(", {0, 0, 0, 0}, false, 0},"); 453 454 flush(); 455 snprintf(scratch, sizeof(scratch), "// %d: ", count); 456 mPrefix = "\n// "; 457 print(scratch); 458 //print(renderer ? renderer->information().ascii() : "NO_RENDER_INFO"); 459 if (node->isElementNode()) { 460 Element* element = static_cast<Element*>(node); 461 NamedNodeMap* attrs = element->attributes(); 462 unsigned length = attrs->length(); 463 if (length > 0) { 464 newLine(); 465 print("// attr: "); 466 for (unsigned i = 0; i < length; i++) { 467 Attribute* a = attrs->attributeItem(i); 468 attr(a->localName(), a->value()); 469 } 470 } 471 } 472 count++; 473 newLine(); 474 #if USE(ACCELERATED_COMPOSITING) 475 if (renderer && layer) { 476 RenderLayerBacking* back = layer->backing(); 477 GraphicsLayerAndroid* grLayer = static_cast 478 <GraphicsLayerAndroid*>(back ? back->graphicsLayer() : 0); 479 LayerAndroid* aLayer = grLayer ? grLayer->contentLayer() : 0; 480 const SkPicture* pict = aLayer ? aLayer->picture() : 0; 481 snprintf(scratch, sizeof(scratch), "// layer:%p back:%p" 482 " gLayer:%p aLayer:%p pict:%p", layer, back, grLayer, 483 aLayer, pict); 484 print(scratch); 485 newLine(); 486 } 487 #endif 488 } while ((node = node->traverseNextNode()) != NULL); 489 DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1); 490 DUMP_NAV_LOGD("\n"); 491 DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = %d;\n\n", name, count - 1); 492 // look for rects with multiple parts 493 node = doc; 494 count = 1; 495 bool hasRectParts = false; 496 int globalOffsetX, globalOffsetY; 497 GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); 498 do { 499 IntRect rect; 500 bool _isFocusable = isFocusable(node) || (node->isTextNode() 501 && node->getRect().isEmpty() == false 502 ); 503 int nodeIndex = count++; 504 if (_isFocusable == false) 505 continue; 506 RenderObject* renderer = node->renderer(); 507 if (renderer == NULL) 508 continue; 509 WTF::Vector<IntRect> rects; 510 IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); 511 IntRect focusBounds = IntRect(0, 0, INT_MAX, INT_MAX); 512 IntRect* rectPtr = &focusBounds; 513 if (node->isTextNode()) { 514 Text* textNode = (Text*) node; 515 if (CacheBuilder::ConstructTextRects(textNode, 0, textNode, 516 INT_MAX, globalOffsetX, globalOffsetY, rectPtr, 517 clipBounds, &rects) == false) 518 continue; 519 } else { 520 IntRect nodeBounds = node->getRect(); 521 if (CacheBuilder::ConstructPartRects(node, nodeBounds, rectPtr, 522 globalOffsetX, globalOffsetY, &rects) == false) 523 continue; 524 } 525 unsigned arraySize = rects.size(); 526 if (arraySize > 1 || (arraySize == 1 && (rectPtr->width() != rect.width())) || 527 rectPtr->height() != rect.height()) { 528 if (hasRectParts == false) { 529 DUMP_NAV_LOGD("static DebugTestRectPart TEST%s_RECTPARTS[] = {\n", name); 530 hasRectParts = true; 531 } 532 if (node->isTextNode() == false) { 533 unsigned rectIndex = 0; 534 for (; rectIndex < arraySize; rectIndex++) { 535 rectPtr = &rects.at(rectIndex); 536 DUMP_NAV_LOGD("{ %d, %d, %d, %d, %d }, // %d\n", nodeIndex, 537 rectPtr->x(), rectPtr->y(), rectPtr->width(), 538 rectPtr->height(), rectIndex + 1); 539 } 540 } else { 541 RenderText* renderText = (RenderText*) node->renderer(); 542 InlineTextBox* textBox = renderText->firstTextBox(); 543 unsigned rectIndex = 0; 544 while (textBox) { 545 FloatPoint pt = renderText->localToAbsolute(); 546 IntRect rect = textBox->selectionRect((int) pt.x(), (int) pt.y(), 0, INT_MAX); 547 mIndex = 0; 548 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "{ %d, %d, %d, %d, %d", 549 nodeIndex, rect.x(), rect.y(), rect.width(), rect.height()); 550 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d", 551 textBox->len(), 0 /*textBox->selectionHeight()*/, 552 0 /*textBox->selectionTop()*/); 553 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d", 554 0 /*textBox->spaceAdd()*/, textBox->start(), 0 /*textBox->textPos()*/); 555 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d, %d, %d, %d", 556 textBox->x(), textBox->y(), textBox->width(), textBox->height()); 557 int baseline = textBox->renderer()->style(textBox->isFirstLineStyle())->font().ascent(); 558 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, ", %d }, // %d ", 559 baseline, ++rectIndex); 560 wideString(node->textContent().characters() + textBox->start(), textBox->len(), true); 561 DUMP_NAV_LOGD("%.*s\n", mIndex, mBuffer); 562 textBox = textBox->nextTextBox(); 563 } 564 } 565 } 566 } while ((node = node->traverseNextNode()) != NULL); 567 if (hasRectParts) 568 DUMP_NAV_LOGD("{0}\n};\n\n"); 569 else 570 DUMP_NAV_LOGD("static DebugTestRectPart* TEST%s_RECTPARTS = NULL;\n", name); 571 } 572 int contentsWidth = layer->width(); 573 int contentsHeight = layer->height(); 574 DUMP_NAV_LOGD("static int TEST%s_FOCUS = %d;\n", name, focusIndex); 575 DUMP_NAV_LOGD("static int TEST%s_WIDTH = %d;\n", name, contentsWidth); 576 DUMP_NAV_LOGD("static int TEST%s_HEIGHT = %d;\n\n", name, contentsHeight); 577 } 578 579 bool CacheBuilder::Debug::isFocusable(Node* node) { 580 if (node->hasTagName(HTMLNames::areaTag)) 581 return true; 582 if (node->renderer() == false) 583 return false; 584 if (node->isKeyboardFocusable(NULL)) 585 return true; 586 if (node->isMouseFocusable()) 587 return true; 588 if (node->isFocusable()) 589 return true; 590 if (CacheBuilder::AnyIsClick(node)) 591 return false; 592 if (CacheBuilder::HasTriggerEvent(node)) 593 return true; 594 return false; 595 } 596 597 void CacheBuilder::Debug::localName(Node* node) { 598 const AtomicString& local = node->localName(); 599 if (node->isTextNode()) 600 print("\"#text\""); 601 else 602 wideString(local.characters(), local.length(), false); 603 print(", "); 604 } 605 606 void CacheBuilder::Debug::newLine(int indent) { 607 if (mPrefix[0] != '\n') 608 print(&mPrefix[0], 1); 609 flush(); 610 int lastnewline = mIndex - 1; 611 while (lastnewline >= 0 && mBuffer[lastnewline] != '\n') 612 lastnewline--; 613 lastnewline++; 614 char* buffer = mBuffer; 615 if (lastnewline > 0) { 616 DUMP_NAV_LOGD("%.*s", lastnewline, buffer); 617 mIndex -= lastnewline; 618 buffer += lastnewline; 619 } 620 size_t prefixLen = strlen(mPrefix); 621 int minPrefix = prefixLen - 1; 622 while (minPrefix >= 0 && mPrefix[minPrefix] != '\n') 623 minPrefix--; 624 minPrefix = prefixLen - minPrefix - 1; 625 if (mIndex > minPrefix) 626 DUMP_NAV_LOGD("%.*s\n", mIndex, buffer); 627 mIndex = 0; 628 setIndent(indent); 629 } 630 631 int CacheBuilder::Debug::ParentIndex(Node* node, int count, Node* parent) 632 { 633 if (parent == NULL) 634 return -1; 635 ASSERT(node != parent); 636 int result = count; 637 Node* previous = node; 638 do { 639 result--; 640 previous = previous->traversePreviousNode(); 641 } while (previous && previous != parent); 642 if (previous != NULL) 643 return result; 644 result = count; 645 do { 646 result++; 647 } while ((node = node->traverseNextNode()) != NULL && node != parent); 648 if (node != NULL) 649 return result; 650 ASSERT(0); 651 return -1; 652 } 653 654 void CacheBuilder::Debug::print(const char* name) { 655 print(name, strlen(name)); 656 } 657 658 void CacheBuilder::Debug::print(const char* name, unsigned len) { 659 do { 660 if (mIndex + len >= DEBUG_BUFFER_SIZE) 661 flush(); 662 int copyLen = mIndex + len < DEBUG_BUFFER_SIZE ? 663 len : DEBUG_BUFFER_SIZE - mIndex; 664 memcpy(&mBuffer[mIndex], name, copyLen); 665 mIndex += copyLen; 666 name += copyLen; 667 len -= copyLen; 668 } while (len > 0); 669 mBuffer[mIndex] = '\0'; 670 } 671 672 void CacheBuilder::Debug::setIndent(int indent) 673 { 674 char scratch[64]; 675 snprintf(scratch, sizeof(scratch), "%.*s", indent, 676 " "); 677 print(scratch); 678 } 679 680 void CacheBuilder::Debug::uChar(const UChar* name, unsigned len, bool hex) { 681 const UChar* end = name + len; 682 bool wroteHex = false; 683 while (name < end) { 684 unsigned ch = *name++; 685 if (ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0) 686 ch = ' '; 687 if (ch < ' ' || ch == 0x7f) { 688 if (hex) { 689 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "\\x%02x", ch); 690 wroteHex = true; 691 } else 692 mBuffer[mIndex++] = '?'; 693 } else if (ch >= 0x80) { 694 if (hex) { 695 if (ch < 0x800) 696 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, 697 "\\x%02x\\x%02x", ch >> 6 | 0xc0, (ch & 0x3f) | 0x80); 698 else 699 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, 700 "\\x%02x\\x%02x\\x%02x", ch >> 12 | 0xe0, 701 (ch >> 6 & 0x3f) | 0x80, (ch & 0x3f) | 0x80); 702 wroteHex = true; 703 } else 704 mBuffer[mIndex++] = '?'; 705 } else { 706 if (wroteHex && WTF::isASCIIHexDigit((UChar) ch)) 707 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, 708 "\" \""); 709 else if (ch == '"' || ch == '\\') 710 mBuffer[mIndex++] = '\\'; 711 mBuffer[mIndex++] = ch; 712 wroteHex = false; 713 } 714 if (mIndex + 1 >= DEBUG_BUFFER_SIZE) 715 flush(); 716 } 717 flush(); 718 } 719 720 void CacheBuilder::Debug::validateFrame() { 721 Frame* frame = frameAnd(); 722 Page* page = frame->page(); 723 ASSERT(page); 724 ASSERT((int) page > 0x10000); 725 Frame* child = frame->tree()->firstChild(); 726 while (child) { 727 Builder(child)->mDebug.validateFrame(); 728 child = child->tree()->nextSibling(); 729 } 730 } 731 732 void CacheBuilder::Debug::wideString(const UChar* chars, int length, bool hex) { 733 if (length == 0) 734 print("\"\""); 735 else { 736 print("\""); 737 uChar(chars, length, hex); 738 print("\""); 739 } 740 } 741 742 void CacheBuilder::Debug::wideString(const String& str) { 743 wideString(str.characters(), str.length(), false); 744 } 745 746 #endif // DUMP_NAV_CACHE 747 748 CacheBuilder::CacheBuilder() 749 { 750 mAllowableTypes = ALL_CACHEDNODE_BITS; 751 #ifdef DUMP_NAV_CACHE_USING_PRINTF 752 gNavCacheLogFile = NULL; 753 #endif 754 } 755 756 void CacheBuilder::adjustForColumns(const ClipColumnTracker& track, 757 CachedNode* node, IntRect* bounds) 758 { 759 int x = 0; 760 int y = 0; 761 int tx = track.mBounds.x(); 762 int ty = track.mBounds.y(); 763 int columnGap = track.mColumnGap; 764 size_t limit = track.mColumns->size(); 765 for (size_t index = 0; index < limit; index++) { 766 IntRect column = track.mColumns->at(index); 767 column.move(tx, ty); 768 IntRect test = *bounds; 769 test.move(x, y); 770 if (column.contains(test)) { 771 if ((x | y) == 0) 772 return; 773 *bounds = test; 774 node->move(x, y); 775 return; 776 } 777 int xOffset = column.width() + columnGap; 778 x += track.mDirection == LTR ? xOffset : -xOffset; 779 y -= column.height(); 780 } 781 } 782 783 // Checks if a node has one of event listener types. 784 bool CacheBuilder::NodeHasEventListeners(Node* node, AtomicString* eventTypes, int length) { 785 for (int i = 0; i < length; ++i) { 786 if (!node->getEventListeners(eventTypes[i]).isEmpty()) 787 return true; 788 } 789 return false; 790 } 791 792 bool CacheBuilder::AnyChildIsClick(Node* node) 793 { 794 AtomicString eventTypes[5] = { 795 eventNames().clickEvent, 796 eventNames().mousedownEvent, 797 eventNames().mouseupEvent, 798 eventNames().keydownEvent, 799 eventNames().keyupEvent 800 }; 801 802 Node* child = node->firstChild(); 803 while (child != NULL) { 804 if (child->isFocusable() || 805 NodeHasEventListeners(child, eventTypes, 5)) 806 return true; 807 if (AnyChildIsClick(child)) 808 return true; 809 child = child->nextSibling(); 810 } 811 return false; 812 } 813 814 bool CacheBuilder::AnyIsClick(Node* node) 815 { 816 if (node->hasTagName(HTMLNames::bodyTag)) 817 return AnyChildIsClick(node); 818 819 AtomicString eventTypeSetOne[4] = { 820 eventNames().mouseoverEvent, 821 eventNames().mouseoutEvent, 822 eventNames().keydownEvent, 823 eventNames().keyupEvent 824 }; 825 826 if (!NodeHasEventListeners(node, eventTypeSetOne, 4)) 827 return false; 828 829 AtomicString eventTypeSetTwo[3] = { 830 eventNames().clickEvent, 831 eventNames().mousedownEvent, 832 eventNames().mouseupEvent 833 }; 834 835 if (NodeHasEventListeners(node, eventTypeSetTwo, 3)) 836 return false; 837 838 return AnyChildIsClick(node); 839 } 840 841 void CacheBuilder::buildCache(CachedRoot* root) 842 { 843 Frame* frame = FrameAnd(this); 844 BuildFrame(frame, frame, root, (CachedFrame*) root); 845 root->finishInit(); // set up frame parent pointers, child pointers 846 setData((CachedFrame*) root); 847 } 848 849 static Node* ParentWithChildren(Node* node) 850 { 851 Node* parent = node; 852 while ((parent = parent->parentNode())) { 853 if (parent->childNodeCount() > 1) 854 return parent; 855 } 856 return 0; 857 } 858 859 // FIXME 860 // Probably this should check for null instead of the caller. If the 861 // Tracker object is the last thing in the dom, checking for null in the 862 // caller in some cases fails to set up Tracker state which may be useful 863 // to the nodes parsed immediately after the tracked noe. 864 static Node* OneAfter(Node* node) 865 { 866 Node* parent = node; 867 Node* sibling = NULL; 868 while ((parent = parent->parentNode()) != NULL) { 869 sibling = parent->nextSibling(); 870 if (sibling != NULL) 871 break; 872 } 873 return sibling; 874 } 875 876 // return true if this renderer is really a pluinview, and it wants 877 // key-events (i.e. focus) 878 static bool checkForPluginViewThatWantsFocus(RenderObject* renderer) { 879 if (renderer->isWidget()) { 880 Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); 881 if (widget && (widget->isPluginView() || widget->isPluginWidget())) { 882 // check if this plugin really wants key events (TODO) 883 return true; 884 } 885 } 886 return false; 887 } 888 889 #if USE(ACCELERATED_COMPOSITING) 890 static void AddLayer(CachedFrame* frame, size_t index, const IntPoint& location, 891 int id) 892 { 893 DBG_NAV_LOGD("frame=%p index=%d loc=(%d,%d) id=%d", frame, index, 894 location.x(), location.y(), id); 895 CachedLayer cachedLayer; 896 cachedLayer.reset(); 897 cachedLayer.setCachedNodeIndex(index); 898 cachedLayer.setOffset(location); 899 cachedLayer.setUniqueId(id); 900 frame->add(cachedLayer); 901 } 902 #endif 903 904 // when new focus is found, push it's parent on a stack 905 // as long as more focii are found with the same (grand) parent, note it 906 // (which only requires retrieving the last parent on the stack) 907 // when the parent's last child is found, pop the stack 908 // different from Tracker in that Tracker only pushes focii with children 909 910 // making this work with focus - child focus - grandchild focus is tricky 911 // if I keep the generation number, I may be able to more quickly determine that 912 // a node is a grandchild of the focus's parent 913 // this additionally requires being able to find the grandchild's parent 914 915 // keep nodes that are focusable 916 void CacheBuilder::BuildFrame(Frame* root, Frame* frame, 917 CachedRoot* cachedRoot, CachedFrame* cachedFrame) 918 { 919 WTF::Vector<FocusTracker> tracker(1); // sentinel 920 { 921 FocusTracker* baseTracker = tracker.data(); 922 bzero(baseTracker, sizeof(FocusTracker)); 923 baseTracker->mCachedNodeIndex = -1; 924 } 925 WTF::Vector<LayerTracker> layerTracker(1); // sentinel 926 bzero(layerTracker.data(), sizeof(LayerTracker)); 927 WTF::Vector<ClipColumnTracker> clipTracker(1); // sentinel 928 bzero(clipTracker.data(), sizeof(ClipColumnTracker)); 929 WTF::Vector<TabIndexTracker> tabIndexTracker(1); // sentinel 930 bzero(tabIndexTracker.data(), sizeof(TabIndexTracker)); 931 #if DUMP_NAV_CACHE 932 char* frameNamePtr = cachedFrame->mDebug.mFrameName; 933 Builder(frame)->mDebug.frameName(frameNamePtr, frameNamePtr + 934 sizeof(cachedFrame->mDebug.mFrameName) - 1); 935 *frameNamePtr = '\0'; 936 int nodeIndex = 1; 937 #endif 938 NodeWalk walk; 939 Document* doc = frame->document(); 940 Node* parent = doc; 941 CachedNode cachedParentNode; 942 cachedParentNode.init(parent); 943 #if DUMP_NAV_CACHE 944 cachedParentNode.mDebug.mNodeIndex = nodeIndex; 945 #endif 946 cachedFrame->add(cachedParentNode); 947 Node* node = parent; 948 int cacheIndex = 1; 949 int textInputIndex = 0; 950 Node* focused = doc->focusedNode(); 951 if (focused) 952 cachedRoot->setFocusBounds(focused->getRect()); 953 int globalOffsetX, globalOffsetY; 954 GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY); 955 IntPoint bodyPos = IntPoint(0, 0); 956 while (walk.mMore || (node = node->traverseNextNode()) != NULL) { 957 #if DUMP_NAV_CACHE 958 nodeIndex++; 959 #endif 960 FocusTracker* last = &tracker.last(); 961 int lastChildIndex = cachedFrame->size() - 1; 962 while (node == last->mLastChild) { 963 if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex)) 964 cacheIndex--; 965 tracker.removeLast(); 966 lastChildIndex = last->mCachedNodeIndex; 967 last = &tracker.last(); 968 } 969 do { 970 const ClipColumnTracker* lastClip = &clipTracker.last(); 971 if (node != lastClip->mLastChild) 972 break; 973 clipTracker.removeLast(); 974 } while (true); 975 do { 976 const LayerTracker* lastLayer = &layerTracker.last(); 977 if (node != lastLayer->mLastChild) 978 break; 979 layerTracker.removeLast(); 980 } while (true); 981 do { 982 const TabIndexTracker* lastTabIndex = &tabIndexTracker.last(); 983 if (node != lastTabIndex->mLastChild) 984 break; 985 tabIndexTracker.removeLast(); 986 } while (true); 987 Frame* child = HasFrame(node); 988 CachedNode cachedNode; 989 if (child != NULL) { 990 if (child->document() == NULL) 991 continue; 992 RenderObject* nodeRenderer = node->renderer(); 993 if (nodeRenderer != NULL && nodeRenderer->style()->visibility() == HIDDEN) 994 continue; 995 CachedFrame cachedChild; 996 cachedChild.init(cachedRoot, cacheIndex, child); 997 int childFrameIndex = cachedFrame->childCount(); 998 cachedFrame->addFrame(cachedChild); 999 cachedNode.init(node); 1000 cachedNode.setIndex(cacheIndex++); 1001 cachedNode.setDataIndex(childFrameIndex); 1002 cachedNode.setType(FRAME_CACHEDNODETYPE); 1003 #if DUMP_NAV_CACHE 1004 cachedNode.mDebug.mNodeIndex = nodeIndex; 1005 cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex( 1006 node, nodeIndex, NULL); 1007 #endif 1008 cachedFrame->add(cachedNode); 1009 CachedFrame* childPtr = cachedFrame->lastChild(); 1010 BuildFrame(root, child, cachedRoot, childPtr); 1011 continue; 1012 } 1013 int tabIndex = node->tabIndex(); 1014 Node* lastChild = node->lastChild(); 1015 if (tabIndex <= 0) 1016 tabIndex = tabIndexTracker.last().mTabIndex; 1017 else if (tabIndex > 0 && lastChild) { 1018 DBG_NAV_LOGD("tabIndex=%d node=%p", tabIndex, node); 1019 tabIndexTracker.grow(tabIndexTracker.size() + 1); 1020 TabIndexTracker& indexTracker = tabIndexTracker.last(); 1021 indexTracker.mTabIndex = tabIndex; 1022 indexTracker.mLastChild = OneAfter(lastChild); 1023 } 1024 RenderObject* nodeRenderer = node->renderer(); 1025 bool isTransparent = false; 1026 bool hasCursorRing = true; 1027 if (nodeRenderer != NULL) { 1028 RenderStyle* style = nodeRenderer->style(); 1029 if (style->visibility() == HIDDEN) 1030 continue; 1031 isTransparent = style->hasBackground() == false; 1032 #ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR 1033 hasCursorRing = style->tapHighlightColor().alpha() > 0; 1034 #endif 1035 #if USE(ACCELERATED_COMPOSITING) 1036 if (nodeRenderer->hasLayer()) { 1037 TrackLayer(layerTracker, nodeRenderer, lastChild, 1038 globalOffsetX - bodyPos.x(), globalOffsetY - bodyPos.y()); 1039 size_t size = tracker.size(); 1040 const LayerAndroid* layer = layerTracker.last().mLayer; 1041 if (layer) { 1042 int id = layer->uniqueId(); 1043 IntPoint loc = nodeRenderer-> 1044 absoluteBoundingBoxRect().location(); 1045 loc.move(globalOffsetX, globalOffsetY); 1046 // if this is a child of a CachedNode, add a layer 1047 size_t limit = cachedFrame->layerCount() == 0 ? 0 : 1048 cachedFrame->lastLayer()->cachedNodeIndex(); 1049 for (size_t index = 1; index < tracker.size(); index++) { 1050 const FocusTracker& cursorNode = tracker.at(index); 1051 size_t index = cursorNode.mCachedNodeIndex; 1052 if (index <= limit) { // already added? 1053 DBG_NAV_LOGD("index=%d limit=%d id=%d", index, 1054 limit, id); 1055 continue; 1056 } 1057 DBG_NAV_LOGD("call add layer %d", id); 1058 CachedNode* trackedNode = cachedFrame->getIndex(index); 1059 trackedNode->setIsInLayer(true); 1060 trackedNode->setIsUnclipped(true); 1061 AddLayer(cachedFrame, index, loc, id); 1062 } 1063 } 1064 } 1065 #endif 1066 } 1067 bool more = walk.mMore; 1068 walk.reset(); 1069 // GetGlobalBounds(node, &bounds, false); 1070 bool computeCursorRings = false; 1071 bool hasClip = false; 1072 bool hasMouseOver = false; 1073 bool isUnclipped = false; 1074 bool isFocus = node == focused; 1075 bool takesFocus = false; 1076 int columnGap = 0; 1077 TextDirection direction = LTR; 1078 String exported; 1079 CachedNodeType type = NORMAL_CACHEDNODETYPE; 1080 CachedInput cachedInput; 1081 IntRect bounds; 1082 IntRect absBounds; 1083 IntRect originalAbsBounds; 1084 WTF::Vector<IntRect>* columns = NULL; 1085 if (node->hasTagName(HTMLNames::areaTag)) { 1086 type = AREA_CACHEDNODETYPE; 1087 HTMLAreaElement* area = static_cast<HTMLAreaElement*>(node); 1088 bounds = getAreaRect(area); 1089 originalAbsBounds = bounds; 1090 bounds.move(globalOffsetX, globalOffsetY); 1091 absBounds = bounds; 1092 isUnclipped = true; // FIXME: areamaps require more effort to detect 1093 // assume areamaps are always visible for now 1094 takesFocus = true; 1095 goto keepNode; 1096 } 1097 if (nodeRenderer == NULL) 1098 continue; 1099 1100 // some common setup 1101 absBounds = nodeRenderer->absoluteBoundingBoxRect(); 1102 originalAbsBounds = absBounds; 1103 absBounds.move(globalOffsetX, globalOffsetY); 1104 hasClip = nodeRenderer->hasOverflowClip(); 1105 1106 if (node->hasTagName(HTMLNames::bodyTag)) 1107 bodyPos = originalAbsBounds.location(); 1108 if (checkForPluginViewThatWantsFocus(nodeRenderer)) { 1109 bounds = absBounds; 1110 isUnclipped = true; 1111 takesFocus = true; 1112 type = PLUGIN_CACHEDNODETYPE; 1113 goto keepNode; 1114 } 1115 if (nodeRenderer->isRenderBlock()) { 1116 RenderBlock* renderBlock = (RenderBlock*) nodeRenderer; 1117 if (renderBlock->hasColumns()) { 1118 columns = renderBlock->columnRects(); 1119 #ifdef ANDROID_EXPOSE_COLUMN_GAP 1120 columnGap = renderBlock->columnGap(); 1121 #endif 1122 direction = renderBlock->style()->direction(); 1123 } 1124 } 1125 if ((hasClip != false || columns != NULL) && lastChild) { 1126 clipTracker.grow(clipTracker.size() + 1); 1127 ClipColumnTracker& clip = clipTracker.last(); 1128 clip.mBounds = absBounds; 1129 clip.mLastChild = OneAfter(lastChild); 1130 clip.mNode = node; 1131 clip.mColumns = columns; 1132 clip.mColumnGap = columnGap; 1133 clip.mHasClip = hasClip; 1134 clip.mDirection = direction; 1135 if (columns != NULL) { 1136 const IntRect& oRect = ((RenderBox*)nodeRenderer)->visibleOverflowRect(); 1137 clip.mBounds.move(oRect.x(), oRect.y()); 1138 } 1139 } 1140 if (node->isTextNode() && mAllowableTypes != NORMAL_CACHEDNODE_BITS) { 1141 if (last->mSomeParentTakesFocus) // don't look at text inside focusable node 1142 continue; 1143 CachedNodeType checkType; 1144 if (isFocusableText(&walk, more, node, &checkType, 1145 &exported) == false) 1146 continue; 1147 #if DUMP_NAV_CACHE 1148 { 1149 char buffer[DEBUG_BUFFER_SIZE]; 1150 mDebug.init(buffer, sizeof(buffer)); 1151 mDebug.print("text link found: "); 1152 mDebug.wideString(exported); 1153 DUMP_NAV_LOGD("%s\n", buffer); 1154 } 1155 #endif 1156 type = checkType; 1157 // !!! test ! is the following line correctly needed for frames to work? 1158 cachedNode.init(node); 1159 const ClipColumnTracker& clipTrack = clipTracker.last(); 1160 const IntRect& clip = clipTrack.mHasClip ? clipTrack.mBounds : 1161 IntRect(0, 0, INT_MAX, INT_MAX); 1162 if (ConstructTextRects((WebCore::Text*) node, walk.mStart, 1163 (WebCore::Text*) walk.mFinalNode, walk.mEnd, globalOffsetX, 1164 globalOffsetY, &bounds, clip, &cachedNode.mCursorRing) == false) 1165 continue; 1166 absBounds = bounds; 1167 cachedNode.setBounds(bounds); 1168 if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) 1169 continue; 1170 if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) 1171 continue; 1172 computeCursorRings = true; 1173 isUnclipped = true; // FIXME: to hide or partially occlude synthesized links, each 1174 // focus ring will also need the offset and length of characters 1175 // used to produce it 1176 goto keepTextNode; 1177 } 1178 if (node->hasTagName(WebCore::HTMLNames::inputTag)) { 1179 HTMLInputElement* input = static_cast<HTMLInputElement*>(node); 1180 HTMLInputElement::InputType inputType = input->inputType(); 1181 if (input->isTextField()) { 1182 type = TEXT_INPUT_CACHEDNODETYPE; 1183 cachedInput.init(); 1184 cachedInput.setFormPointer(input->form()); 1185 cachedInput.setIsTextField(true); 1186 exported = input->value().threadsafeCopy(); 1187 cachedInput.setMaxLength(input->maxLength()); 1188 cachedInput.setInputType(inputType); 1189 // If this does not need to be threadsafe, we can use crossThreadString(). 1190 // See http://trac.webkit.org/changeset/49160. 1191 cachedInput.setName(input->name().string().threadsafeCopy()); 1192 // can't detect if this is drawn on top (example: deviant.com login parts) 1193 isUnclipped = isTransparent; 1194 } else if (inputType == HTMLInputElement::HIDDEN) 1195 continue; 1196 } else if (node->hasTagName(HTMLNames::textareaTag)) { 1197 cachedInput.init(); 1198 type = TEXT_INPUT_CACHEDNODETYPE; 1199 HTMLTextAreaElement* area = static_cast<HTMLTextAreaElement*>(node); 1200 cachedInput.setFormPointer(area->form()); 1201 // Although technically it is not an HTMLInputElement, and therefore 1202 // has no InputType, this one is the most appropriate. 1203 cachedInput.setInputType(HTMLInputElement::TEXT); 1204 cachedInput.setIsTextField(false); 1205 exported = area->value().threadsafeCopy(); 1206 } else if (node->hasTagName(HTMLNames::aTag)) { 1207 const HTMLAnchorElement* anchorNode = 1208 (const HTMLAnchorElement*) node; 1209 if (!anchorNode->isFocusable() && !HasTriggerEvent(node)) 1210 continue; 1211 if (node->disabled()) 1212 continue; 1213 hasMouseOver = NodeHasEventListeners(node, &eventNames().mouseoverEvent, 1); 1214 type = ANCHOR_CACHEDNODETYPE; 1215 KURL href = anchorNode->href(); 1216 if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string())) 1217 // Set the exported string for all non-javascript anchors. 1218 exported = href.string().threadsafeCopy(); 1219 } 1220 if (type == TEXT_INPUT_CACHEDNODETYPE) { 1221 RenderTextControl* renderText = 1222 static_cast<RenderTextControl*>(nodeRenderer); 1223 if (isFocus) 1224 cachedRoot->setSelection(renderText->selectionStart(), renderText->selectionEnd()); 1225 // FIXME: Would it be better to use (float) size()? 1226 // FIXME: Are we sure there will always be a style and font, and it's correct? 1227 RenderStyle* style = nodeRenderer->style(); 1228 if (style) { 1229 isUnclipped |= !style->hasAppearance(); 1230 cachedInput.setTextSize(style->fontSize()); 1231 cachedInput.setIsRtlText(style->direction() == RTL 1232 || style->textAlign() == WebCore::RIGHT 1233 || style->textAlign() == WebCore::WEBKIT_RIGHT); 1234 } 1235 } 1236 takesFocus = true; 1237 bounds = absBounds; 1238 if (type != ANCHOR_CACHEDNODETYPE) { 1239 bool isFocusable = node->isKeyboardFocusable(NULL) || 1240 node->isMouseFocusable() || node->isFocusable(); 1241 if (isFocusable == false) { 1242 if (node->disabled()) 1243 continue; 1244 bool overOrOut = HasOverOrOut(node); 1245 bool hasTrigger = HasTriggerEvent(node); 1246 if (overOrOut == false && hasTrigger == false) 1247 continue; 1248 takesFocus = hasTrigger; 1249 } 1250 } 1251 computeCursorRings = true; 1252 keepNode: 1253 cachedNode.init(node); 1254 if (computeCursorRings == false) { 1255 cachedNode.setBounds(bounds); 1256 cachedNode.mCursorRing.append(bounds); 1257 } else if (ConstructPartRects(node, bounds, &cachedNode.mBounds, 1258 globalOffsetX, globalOffsetY, &cachedNode.mCursorRing) == false) 1259 continue; 1260 keepTextNode: 1261 IntRect clip = hasClip ? bounds : absBounds; 1262 size_t clipIndex = clipTracker.size(); 1263 if (clipTracker.last().mNode == node) 1264 clipIndex -= 1; 1265 while (--clipIndex > 0) { 1266 const ClipColumnTracker& clipTrack = clipTracker.at(clipIndex); 1267 if (clipTrack.mHasClip == false) { 1268 adjustForColumns(clipTrack, &cachedNode, &absBounds); 1269 continue; 1270 } 1271 const IntRect& parentClip = clipTrack.mBounds; 1272 if (hasClip == false && type == ANCHOR_CACHEDNODETYPE) 1273 clip = parentClip; 1274 else 1275 clip.intersect(parentClip); 1276 hasClip = true; 1277 } 1278 if (hasClip) { 1279 if (clip.isEmpty()) 1280 continue; // skip this node if clip prevents all drawing 1281 else if (cachedNode.clip(clip) == false) 1282 continue; // skip this node if outside of the clip 1283 } 1284 bool isInLayer = false; 1285 #if USE(ACCELERATED_COMPOSITING) 1286 // FIXME: does not work for area rects 1287 LayerAndroid* layer = layerTracker.last().mLayer; 1288 if (layer) { 1289 const IntRect& layerClip = layerTracker.last().mBounds; 1290 if (!layerClip.isEmpty() && !cachedNode.clip(layerClip)) { 1291 DBG_NAV_LOGD("skipped on layer clip %d", cacheIndex); 1292 continue; // skip this node if outside of the clip 1293 } 1294 isInLayer = true; 1295 isUnclipped = true; // assume that layers do not have occluded nodes 1296 AddLayer(cachedFrame, cachedFrame->size(), layerClip.location(), 1297 layer->uniqueId()); 1298 } 1299 #endif 1300 cachedNode.setNavableRects(); 1301 cachedNode.setExport(exported); 1302 cachedNode.setHasCursorRing(hasCursorRing); 1303 cachedNode.setHasMouseOver(hasMouseOver); 1304 cachedNode.setHitBounds(absBounds); 1305 cachedNode.setIndex(cacheIndex); 1306 cachedNode.setIsFocus(isFocus); 1307 cachedNode.setIsInLayer(isInLayer); 1308 cachedNode.setIsTransparent(isTransparent); 1309 cachedNode.setIsUnclipped(isUnclipped); 1310 cachedNode.setOriginalAbsoluteBounds(originalAbsBounds); 1311 cachedNode.setParentIndex(last->mCachedNodeIndex); 1312 cachedNode.setParentGroup(ParentWithChildren(node)); 1313 cachedNode.setTabIndex(tabIndex); 1314 cachedNode.setType(type); 1315 if (type == TEXT_INPUT_CACHEDNODETYPE) { 1316 cachedFrame->add(cachedInput); 1317 cachedNode.setDataIndex(textInputIndex); 1318 textInputIndex++; 1319 } else 1320 cachedNode.setDataIndex(-1); 1321 #if DUMP_NAV_CACHE 1322 cachedNode.mDebug.mNodeIndex = nodeIndex; 1323 cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex( 1324 node, nodeIndex, (Node*) cachedNode.parentGroup()); 1325 #endif 1326 cachedFrame->add(cachedNode); 1327 { 1328 int lastIndex = cachedFrame->size() - 1; 1329 if (node == focused) { 1330 CachedNode* cachedNodePtr = cachedFrame->getIndex(lastIndex); 1331 cachedRoot->setCachedFocus(cachedFrame, cachedNodePtr); 1332 } 1333 if (lastChild != NULL) { 1334 tracker.grow(tracker.size() + 1); 1335 FocusTracker& working = tracker.last(); 1336 working.mCachedNodeIndex = lastIndex; 1337 working.mLastChild = OneAfter(lastChild); 1338 last = &tracker.at(tracker.size() - 2); 1339 working.mSomeParentTakesFocus = last->mSomeParentTakesFocus | takesFocus; 1340 } 1341 } 1342 cacheIndex++; 1343 } 1344 while (tracker.size() > 1) { 1345 FocusTracker* last = &tracker.last(); 1346 int lastChildIndex = cachedFrame->size() - 1; 1347 if (CleanUpContainedNodes(cachedFrame, last, lastChildIndex)) 1348 cacheIndex--; 1349 tracker.removeLast(); 1350 } 1351 } 1352 1353 bool CacheBuilder::CleanUpContainedNodes(CachedFrame* cachedFrame, 1354 const FocusTracker* last, int lastChildIndex) 1355 { 1356 // if outer is body, disable outer 1357 // or if there's more than one inner, disable outer 1358 // or if inner is keyboard focusable, disable outer 1359 // else disable inner by removing it 1360 int childCount = lastChildIndex - last->mCachedNodeIndex; 1361 if (childCount == 0) 1362 return false; 1363 CachedNode* lastCached = cachedFrame->getIndex(last->mCachedNodeIndex); 1364 Node* lastNode = (Node*) lastCached->nodePointer(); 1365 if ((childCount > 1 && lastNode->hasTagName(HTMLNames::selectTag) == false) || 1366 lastNode->hasTagName(HTMLNames::bodyTag) || 1367 lastNode->hasTagName(HTMLNames::formTag)) { 1368 lastCached->setBounds(IntRect(0, 0, 0, 0)); 1369 lastCached->mCursorRing.clear(); 1370 lastCached->setNavableRects(); 1371 return false; 1372 } 1373 CachedNode* onlyChildCached = cachedFrame->lastNode(); 1374 Node* onlyChild = (Node*) onlyChildCached->nodePointer(); 1375 bool outerIsMouseMoveOnly = 1376 lastNode->isKeyboardFocusable(NULL) == false && 1377 lastNode->isMouseFocusable() == false && 1378 lastNode->isFocusable() == false && 1379 HasOverOrOut(lastNode) == true && 1380 HasTriggerEvent(lastNode) == false; 1381 if (onlyChildCached->parent() == lastCached) 1382 onlyChildCached->setParentIndex(lastCached->parentIndex()); 1383 if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL)) 1384 *lastCached = *onlyChildCached; 1385 cachedFrame->removeLast(); 1386 return true; 1387 } 1388 1389 Node* CacheBuilder::currentFocus() const 1390 { 1391 Frame* frame = FrameAnd(this); 1392 Document* doc = frame->document(); 1393 if (doc != NULL) { 1394 Node* focus = doc->focusedNode(); 1395 if (focus != NULL) 1396 return focus; 1397 } 1398 Frame* child = frame->tree()->firstChild(); 1399 while (child) { 1400 CacheBuilder* cacheBuilder = Builder(child); 1401 Node* focus = cacheBuilder->currentFocus(); 1402 if (focus) 1403 return focus; 1404 child = child->tree()->nextSibling(); 1405 } 1406 return NULL; 1407 } 1408 1409 static bool strCharCmp(const char* matches, const UChar* test, int wordLength, 1410 int wordCount) 1411 { 1412 for (int index = 0; index < wordCount; index++) { 1413 for (int inner = 0; inner < wordLength; inner++) { 1414 if (matches[inner] != test[inner]) { 1415 matches += wordLength; 1416 goto next; 1417 } 1418 } 1419 return true; 1420 next: 1421 ; 1422 } 1423 return false; 1424 } 1425 1426 static const int stateTwoLetter[] = { 1427 0x02060c00, // A followed by: [KLRSZ] 1428 0x00000000, // B 1429 0x00084001, // C followed by: [AOT] 1430 0x00000014, // D followed by: [CE] 1431 0x00000000, // E 1432 0x00001800, // F followed by: [LM] 1433 0x00100001, // G followed by: [AU] 1434 0x00000100, // H followed by: [I] 1435 0x00002809, // I followed by: [ADLN] 1436 0x00000000, // J 1437 0x01040000, // K followed by: [SY] 1438 0x00000001, // L followed by: [A] 1439 0x000ce199, // M followed by: [ADEHINOPST] 1440 0x0120129c, // N followed by: [CDEHJMVY] 1441 0x00020480, // O followed by: [HKR] 1442 0x00420001, // P followed by: [ARW] 1443 0x00000000, // Q 1444 0x00000100, // R followed by: [I] 1445 0x0000000c, // S followed by: [CD] 1446 0x00802000, // T followed by: [NX] 1447 0x00080000, // U followed by: [T] 1448 0x00080101, // V followed by: [AIT] 1449 0x01200101 // W followed by: [AIVY] 1450 }; 1451 1452 static const char firstIndex[] = { 1453 0, 5, 5, 8, 10, 10, 12, 14, 1454 15, 19, 19, 21, 22, 32, 40, 43, 1455 46, 46, 47, 49, 51, 52, 55, 59 1456 }; 1457 1458 // from http://infolab.stanford.edu/~manku/bitcount/bitcount.html 1459 #define TWO(c) (0x1u << (c)) 1460 #define MASK(c) (((unsigned int)(-1)) / (TWO(TWO(c)) + 1u)) 1461 #define COUNT(x,c) ((x) & MASK(c)) + (((x) >> (TWO(c))) & MASK(c)) 1462 1463 int bitcount (unsigned int n) 1464 { 1465 n = COUNT(n, 0); 1466 n = COUNT(n, 1); 1467 n = COUNT(n, 2); 1468 n = COUNT(n, 3); 1469 return COUNT(n, 4); 1470 } 1471 1472 #undef TWO 1473 #undef MASK 1474 #undef COUNT 1475 1476 static bool isUnicodeSpace(UChar ch) 1477 { 1478 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0; 1479 } 1480 1481 static bool validZip(int stateIndex, const UChar* zipPtr) 1482 { 1483 static const struct { 1484 char mLow; 1485 char mHigh; 1486 char mException1; 1487 char mException2; 1488 } zipRange[] = { 1489 { 99, 99, -1, -1 }, // AK Alaska 1490 { 35, 36, -1, -1 }, // AL Alabama 1491 { 71, 72, -1, -1 }, // AR Arkansas 1492 { 96, 96, -1, -1 }, // AS American Samoa 1493 { 85, 86, -1, -1 }, // AZ Arizona 1494 { 90, 96, -1, -1 }, // CA California 1495 { 80, 81, -1, -1 }, // CO Colorado 1496 { 6, 6, -1, -1 }, // CT Connecticut 1497 { 20, 20, -1, -1 }, // DC District of Columbia 1498 { 19, 19, -1, -1 }, // DE Delaware 1499 { 32, 34, -1, -1 }, // FL Florida 1500 { 96, 96, -1, -1 }, // FM Federated States of Micronesia 1501 { 30, 31, -1, -1 }, // GA Georgia 1502 { 96, 96, -1, -1 }, // GU Guam 1503 { 96, 96, -1, -1 }, // HI Hawaii 1504 { 50, 52, -1, -1 }, // IA Iowa 1505 { 83, 83, -1, -1 }, // ID Idaho 1506 { 60, 62, -1, -1 }, // IL Illinois 1507 { 46, 47, -1, -1 }, // IN Indiana 1508 { 66, 67, 73, -1 }, // KS Kansas 1509 { 40, 42, -1, -1 }, // KY Kentucky 1510 { 70, 71, -1, -1 }, // LA Louisiana 1511 { 1, 2, -1, -1 }, // MA Massachusetts 1512 { 20, 21, -1, -1 }, // MD Maryland 1513 { 3, 4, -1, -1 }, // ME Maine 1514 { 96, 96, -1, -1 }, // MH Marshall Islands 1515 { 48, 49, -1, -1 }, // MI Michigan 1516 { 55, 56, -1, -1 }, // MN Minnesota 1517 { 63, 65, -1, -1 }, // MO Missouri 1518 { 96, 96, -1, -1 }, // MP Northern Mariana Islands 1519 { 38, 39, -1, -1 }, // MS Mississippi 1520 { 55, 56, -1, -1 }, // MT Montana 1521 { 27, 28, -1, -1 }, // NC North Carolina 1522 { 58, 58, -1, -1 }, // ND North Dakota 1523 { 68, 69, -1, -1 }, // NE Nebraska 1524 { 3, 4, -1, -1 }, // NH New Hampshire 1525 { 7, 8, -1, -1 }, // NJ New Jersey 1526 { 87, 88, 86, -1 }, // NM New Mexico 1527 { 88, 89, 96, -1 }, // NV Nevada 1528 { 10, 14, 0, 6 }, // NY New York 1529 { 43, 45, -1, -1 }, // OH Ohio 1530 { 73, 74, -1, -1 }, // OK Oklahoma 1531 { 97, 97, -1, -1 }, // OR Oregon 1532 { 15, 19, -1, -1 }, // PA Pennsylvania 1533 { 6, 6, 0, 9 }, // PR Puerto Rico 1534 { 96, 96, -1, -1 }, // PW Palau 1535 { 2, 2, -1, -1 }, // RI Rhode Island 1536 { 29, 29, -1, -1 }, // SC South Carolina 1537 { 57, 57, -1, -1 }, // SD South Dakota 1538 { 37, 38, -1, -1 }, // TN Tennessee 1539 { 75, 79, 87, 88 }, // TX Texas 1540 { 84, 84, -1, -1 }, // UT Utah 1541 { 22, 24, 20, -1 }, // VA Virginia 1542 { 6, 9, -1, -1 }, // VI Virgin Islands 1543 { 5, 5, -1, -1 }, // VT Vermont 1544 { 98, 99, -1, -1 }, // WA Washington 1545 { 53, 54, -1, -1 }, // WI Wisconsin 1546 { 24, 26, -1, -1 }, // WV West Virginia 1547 { 82, 83, -1, -1 } // WY Wyoming 1548 }; 1549 1550 int zip = zipPtr[0] - '0'; 1551 zip *= 10; 1552 zip += zipPtr[1] - '0'; 1553 int low = zipRange[stateIndex].mLow; 1554 int high = zipRange[stateIndex].mHigh; 1555 if (zip >= low && zip <= high) 1556 return true; 1557 if (zip == zipRange[stateIndex].mException1) 1558 return true; 1559 if (zip == zipRange[stateIndex].mException2) 1560 return true; 1561 return false; 1562 } 1563 1564 #define MAX_PLACE_NAME_LENGTH 25 // the longest allowable one word place name 1565 1566 CacheBuilder::FoundState CacheBuilder::FindAddress(const UChar* chars, 1567 unsigned length, int* start, int* end, bool caseInsensitive) 1568 { 1569 FindState addressState; 1570 FindReset(&addressState); 1571 addressState.mWords[0] = addressState.mStarts[0] = chars; 1572 addressState.mCaseInsensitive = caseInsensitive; 1573 FoundState state = FindPartialAddress(chars, chars, length, &addressState); 1574 if (state == FOUND_PARTIAL && addressState.mProgress == ZIP_CODE && 1575 addressState.mNumberCount == 0) { 1576 addressState.mProgress = FIND_STREET; 1577 state = FindPartialAddress(NULL, NULL, 0, &addressState); 1578 } 1579 *start = addressState.mStartResult; 1580 *end = addressState.mEndResult; 1581 return state; 1582 } 1583 1584 CacheBuilder::FoundState CacheBuilder::FindPartialAddress(const UChar* baseChars, 1585 const UChar* chars, unsigned length, FindState* s) 1586 { 1587 // lower case letters are optional; trailing asterisk is optional 's' 1588 static char const* const longStreetNames[] = { 1589 "\x04" "LleY" "\x04" "NneX" "\x05" "RCade" "\x05" "VEnue" "\x06" "LAMEDA", // A 1590 "\x04" "aYoU" "\x04" "eaCH" "\x03" "eND" "\x05" "LuFf*" "\x05" "oTtoM" 1591 "\x08" "ouLeVarD" "\x05" "Ranch" "\x05" "RidGe" "\x05" "RooK*" 1592 "\x04" "urG*" "\x05" "YPass" "\x07" "roadWAY", // B 1593 "\x05" "AMINO" 1594 "\x03" "amP" "\x05" "anYoN" "\x03" "aPE" "\x07" "auSeWaY" "\x06" "enTeR*" 1595 "\x06" "IRcle*" "\x05" "LiFf*" "\x03" "LuB" "\x05" "oMmoN" "\x06" "ORner*" 1596 "\x05" "ouRSE" "\x05" "ourT*" "\x04" "oVe*" "\x04" "ReeK" "\x07" "REScent" 1597 "\x04" "ReST" "\x07" "ROSSING" "\x08" "ROSSROAD" "\x04" "URVe" 1598 "\x05" "AMINO" "\x06" "IRCULO" "\x07" "REScent", // C 1599 "\x03" "aLe" "\x02" "aM" "\x05" "iVide" "\x05" "Rive*", // D 1600 "\x06" "STate*" "\x09" "XPresswaY" "\x09" "XTension*", // E 1601 "\x04" "ALL*" "\x04" "eRrY" "\x05" "ieLD*" "\x04" "LaT*" "\x04" "oRD*" 1602 "\x05" "oReST" "\x05" "oRGe*" "\x04" "oRK*" "\x03" "orT" "\x06" "reeWaY", // F 1603 "\x06" "arDeN*" "\x06" "aTeWaY" "\x04" "LeN*" "\x05" "ReeN*" "\x05" "RoVe*", // G 1604 "\x06" "arBoR*" "\x04" "aVeN" "\x06" "eighTS" "\x06" "ighWaY" "\x04" "iLl*" 1605 "\x05" "OLloW", // H 1606 "\x04" "NLeT" "\x06" "Sland*" "\x03" "SLE", // I 1607 "\x08" "unCTion*", // J 1608 "\x03" "eY*" "\x05" "NoLl*", // K 1609 "\x04" "aKe*" "\x03" "AND" "\x06" "aNDinG" "\x03" "aNe" "\x05" "iGhT*" 1610 "\x03" "oaF" "\x04" "oCK*" "\x04" "oDGe" "\x03" "OOP", // L 1611 "\x03" "ALL" "\x05" "aNoR*" "\x06" "eaDoW*" "\x03" "EWS" "\x04" "iLl*" 1612 "\x06" "iSsioN" "\x07" "oTorWaY" "\x04" "ounT" "\x08" "ounTaiN*", // M 1613 "\x03" "eCK", // N 1614 "\x06" "RCHard" "\x03" "VAL" "\x07" "verPASs", // O 1615 "\x04" "ARK*" "\x07" "arKWaY*" "\x03" "ASS" "\x06" "aSsaGE" "\x03" "ATH" 1616 "\x03" "IKE" "\x04" "iNE*" "\x04" "Lace" "\x05" "LaiN*" "\x04" "LaZa" 1617 "\x05" "oinT*" "\x04" "oRT*" "\x06" "Rairie" "\x06" "RIVADA", // P 1618 NULL, 1619 "\x05" "ADiaL" "\x03" "AMP" "\x04" "aNCH" "\x05" "aPiD*" 1620 "\x03" "eST" 1621 "\x05" "iDGe*" "\x04" "IVer" "\x04" "oaD*" "\x04" "ouTE" "\x02" "OW" 1622 "\x02" "UE" "\x02" "UN", // R 1623 "\x05" "HoaL*" "\x05" "HoRe*" "\x05" "KyWaY" "\x06" "PrinG*" "\x04" "PUR*" 1624 "\x06" "Quare*" "\x06" "TAtion" "\x08" "TRAvenue" "\x05" "TReaM" 1625 "\x06" "Treet*" "\x05" "uMmiT" "\x07" "PeeDWaY", // S 1626 "\x06" "ERrace" "\x09" "hRoughWaY" "\x04" "RaCE" "\x04" "RAcK" "\x09" "RaFficwaY" 1627 "\x04" "RaiL" "\x05" "UNneL" "\x07" "urnPiKE", // T 1628 "\x08" "nderPASs" "\x05" "Nion*", // U 1629 "\x06" "aLleY*" "\x06" "IAduct" "\x04" "ieW*" "\x07" "iLlaGe*" "\x04" "iLle" 1630 "\x04" "ISta", // V 1631 "\x04" "ALK*" "\x03" "ALL" "\x03" "AY*" "\x04" "eLl*", // W 1632 "\x03" "ING" "\x02" "RD", // X 1633 }; 1634 1635 static char const* const longStateNames[] = { 1636 "\x8e" "la" "\x85" "bama" "\x02" "\x84" "ska" "\x01" "\x8f" "merican Samoa" "\x04" 1637 "\x91" "r" "\x86" "izona" "\x05" "\x87" "kansas" "\x03", 1638 NULL, 1639 "\x8b" "alifornia" "\x06" "\x95" "o" "\x87" "lorado" "\x07" "\x8a" "nnecticut" "\x08", 1640 "\x89" "elaware" "\x0a" "\x95" "istrict of Columbia" "\x09", 1641 NULL, 1642 "\x9f" "ederated States of Micronesia" "\x0c" "\x88" "lorida" "\x0b", 1643 "\x85" "uam" "\x0e" "\x88" "eorgia" "\x0d", 1644 "\x87" "awaii" "\x0f", 1645 "\x86" "daho" "\x11" "\x89" "llinois" "\x12" "\x88" "ndiana" "\x13" "\x85" 1646 "owa" "\x10", 1647 NULL, 1648 "\x87" "ansas" "\x14" "\x89" "entucky" "\x15", 1649 "\x8a" "ouisiana" "\x16", 1650 "\x86" "aine" "\x19" "\x99" "ar" "\x8e" "shall Islands" "\x1a" "\x86" "yland" "\x18" 1651 "\x8e" "assachusetts" "\x17" "\x93" "i" "\x87" "chigan" "\x1b" 1652 "\x88" "nnesota" "\x1c" "\x93" "iss" "\x88" "issippi" "\x1f" "\x85" 1653 "ouri" "\x1d" "\x88" "ontana" "\x20", 1654 "\x90" "e" "\x87" "braska" "\x23" "\x85" "vada" "\x27" "\xa5" "ew " "\x8a" 1655 "Hampshire" "\x24" "\x87" "Jersey" "\x25" "\x87" "Mexico" "\x26" 1656 "\x85" "York" "\x28" "\x98" "orth " "\x89" "Carolina" "\x21" "\x87" 1657 "Dakota" "\x22" "\x99" "orthern Mariana Islands" "\x1e", 1658 "\x85" "hio" "\x29" "\x89" "klahoma" "\x2a" "\x87" "regon" "\x2b", 1659 "\x86" "alau" "\x2e" "\x8d" "ennsylvania" "\x2c" "\x8c" "uerto Rico" "\x2d", 1660 NULL, 1661 "\x8d" "hode Island" "\x2f", 1662 "\x98" "outh " "\x89" "Carolina" "\x30" "\x87" "Dakota" "\x31", 1663 "\x90" "e" "\x88" "nnessee" "\x32" "\x84" "xas" "\x33", 1664 "\x85" "tah" "\x34", 1665 "\x88" "ermont" "\x37" "\x94" "irgin" "\x89" " Islands" "\x36" "\x83" "ia" "\x35", 1666 "\x8b" "ashington" "\x38" "\x8e" "est Virginia" "\x3a" "\x8a" "isconsin" "\x39" 1667 "\x88" "yoming" "\x3b" 1668 }; 1669 1670 #if 0 // DEBUG_NAV_UI 1671 static char const* const progressNames[] = { 1672 "NO_ADDRESS", 1673 "SKIP_TO_SPACE", 1674 "HOUSE_NUMBER", 1675 "NUMBER_TRAILING_SPACE", 1676 "ADDRESS_LINE", 1677 "STATE_NAME", 1678 "SECOND_HALF", 1679 "ZIP_CODE", 1680 "PLUS_4", 1681 "FIND_STREET" 1682 }; 1683 #endif 1684 // strategy: US only support at first 1685 // look for a 1 - 5 digit number for a street number (no support for 'One Microsoft Way') 1686 // ignore if preceded by '#', Suite, Ste, Rm 1687 // look for two or more words (up to 5? North Frank Lloyd Wright Blvd) 1688 // note: "The Circle at North Hills St." has six words, and a lower 'at' -- allow at, by, of, in, the, and, ... ? 1689 // if a word starts with a lowercase letter, no match 1690 // allow: , . - # / (for 1/2) ' " 1691 // don't look for street name type yet 1692 // look for one or two delimiters to represent possible 2nd addr line and city name 1693 // look for either full state name, or state two letters, and/or zip code (5 or 9 digits) 1694 // now look for street suffix, either in full or abbreviated form, with optional 's' if there's an asterisk 1695 1696 s->mCurrentStart = chars; 1697 s->mEnd = chars + length; 1698 int candIndex = 0; 1699 bool retryState; 1700 bool mustBeAllUpper = false; 1701 bool secondHalf = false; 1702 chars -= 1; 1703 UChar ch = s->mCurrent; 1704 while (++chars <= s->mEnd) { 1705 UChar prior = ch; 1706 ch = chars < s->mEnd ? *chars : ' '; 1707 switch (s->mProgress) { 1708 case NO_ADDRESS: 1709 if (WTF::isASCIIDigit(ch) == false) { 1710 if (ch != 'O') // letter 'O', not zero 1711 continue; 1712 if (s->mEnd - chars < 3) 1713 continue; 1714 prior = *++chars; 1715 ch = *++chars; 1716 if ((prior != 'n' || ch != 'e') && (prior != 'N' || ch != 'E')) 1717 continue; 1718 if (isUnicodeSpace(*++chars) == false) 1719 continue; 1720 s->mProgress = ADDRESS_LINE; 1721 s->mStartResult = chars - 3 - s->mCurrentStart; 1722 continue; 1723 } 1724 if (isUnicodeSpace(prior) == false) { 1725 s->mProgress = SKIP_TO_SPACE; 1726 continue; 1727 } 1728 s->mNumberCount = 1; 1729 s->mProgress = HOUSE_NUMBER; 1730 s->mStartResult = chars - s->mCurrentStart; 1731 continue; 1732 case SKIP_TO_SPACE: 1733 if (isUnicodeSpace(ch) == false) 1734 continue; 1735 break; 1736 case HOUSE_NUMBER: 1737 if (WTF::isASCIIDigit(ch)) { 1738 if (++s->mNumberCount >= 6) 1739 s->mProgress = SKIP_TO_SPACE; 1740 continue; 1741 } 1742 if (WTF::isASCIIUpper(ch)) { // allow one letter after house number, e.g. 12A SKOLFIELD PL, HARPSWELL, ME 04079 1743 if (WTF::isASCIIDigit(prior) == false) 1744 s->mProgress = SKIP_TO_SPACE; 1745 continue; 1746 } 1747 if (ch == '-') { 1748 if (s->mNumberCount > 0) { // permit 21-23 ELM ST 1749 ++s->mNumberCount; 1750 continue; 1751 } 1752 } 1753 s->mNumberCount = 0; 1754 s->mProgress = NUMBER_TRAILING_SPACE; 1755 case NUMBER_TRAILING_SPACE: 1756 if (isUnicodeSpace(ch)) 1757 continue; 1758 if (0 && WTF::isASCIIDigit(ch)) { 1759 s->mNumberCount = 1; 1760 s->mProgress = HOUSE_NUMBER; 1761 s->mStartResult = chars - s->mCurrentStart; 1762 continue; 1763 } 1764 if (WTF::isASCIIDigit(ch) == false && WTF::isASCIIUpper(ch) == false) 1765 break; 1766 s->mProgress = ADDRESS_LINE; 1767 case ADDRESS_LINE: 1768 if (WTF::isASCIIAlpha(ch) || ch == '\'' || ch == '-' || ch == '&' || ch == '(' || ch == ')') { 1769 if (++s->mLetterCount > 1) { 1770 s->mNumberWords &= ~(1 << s->mWordCount); 1771 continue; 1772 } 1773 if (s->mNumberCount >= 5) 1774 break; 1775 // FIXME: the test below was added to give up on a non-address, but it 1776 // incorrectly discards addresses where the house number is in one node 1777 // and the street name is in the next; I don't recall what the failing case 1778 // is that suggested this fix. 1779 // if (s->mWordCount == 0 && s->mContinuationNode) 1780 // return FOUND_NONE; 1781 s->newWord(baseChars, chars); 1782 if (WTF::isASCIILower(ch) && s->mNumberCount == 0) 1783 s->mFirstLower = chars; 1784 s->mNumberCount = 0; 1785 if (WTF::isASCIILower(ch) || (WTF::isASCIIAlpha(ch) == false && ch != '-')) 1786 s->mNumberWords &= ~(1 << s->mWordCount); 1787 s->mUnparsed = true; 1788 continue; 1789 } else if (s->mLetterCount >= MAX_PLACE_NAME_LENGTH) { 1790 break; 1791 } else if (s->mFirstLower != NULL) { 1792 if (s->mCaseInsensitive) 1793 goto resetWord; 1794 size_t length = chars - s->mFirstLower; 1795 if (length > 3) 1796 break; 1797 if (length == 3 && strCharCmp("and" "the", s->mFirstLower, 3, 2) == false) 1798 break; 1799 if (length == 2 && strCharCmp("at" "by" "el" "in" "of", s->mFirstLower, 2, 5) == false) 1800 break; 1801 goto resetWord; 1802 } 1803 if (ch == ',' || ch == '*') { // delimits lines 1804 // asterisk as delimiter: http://www.sa.sc.edu/wellness/members.html 1805 if (++s->mLineCount > 5) 1806 break; 1807 goto lookForState; 1808 } 1809 if (isUnicodeSpace(ch) || prior == '-') { 1810 lookForState: 1811 if (s->mUnparsed == false) 1812 continue; 1813 const UChar* candidate = s->mWords[s->mWordCount]; 1814 UChar firstLetter = candidate[0]; 1815 if (WTF::isASCIIUpper(firstLetter) == false && WTF::isASCIIDigit(firstLetter) == false) 1816 goto resetWord; 1817 s->mWordCount++; 1818 if ((s->mWordCount == 2 && s->mNumberWords == 3 && WTF::isASCIIDigit(s->mWords[1][1])) || // choose second of 8888 333 Main 1819 (s->mWordCount >= sizeof(s->mWords) / sizeof(s->mWords[0]) - 1)) { // subtract 1 since state names may have two parts 1820 // search for simple number already stored since first potential house # didn't pan out 1821 if (s->mNumberWords == 0) 1822 break; 1823 int shift = 0; 1824 while ((s->mNumberWords & (1 << shift)) == 0) 1825 shift++; 1826 s->mNumberWords >>= ++shift; 1827 if (s->mBases[0] != s->mBases[shift]) // if we're past the original node, bail 1828 break; 1829 s->shiftWords(shift); 1830 s->mStartResult = s->mWords[0] - s->mStarts[0]; 1831 s->mWordCount -= shift; 1832 // FIXME: need to adjust lineCount to account for discarded delimiters 1833 } 1834 if (s->mWordCount < 4) 1835 goto resetWord; 1836 firstLetter -= 'A'; 1837 if (firstLetter > 'W' - 'A') 1838 goto resetWord; 1839 UChar secondLetter = candidate[1]; 1840 if (prior == '-') 1841 s->mLetterCount--; // trim trailing dashes, to accept CA-94043 1842 if (s->mLetterCount == 2) { 1843 secondLetter -= 'A'; 1844 if (secondLetter > 'Z' - 'A') 1845 goto resetWord; 1846 if ((stateTwoLetter[firstLetter] & 1 << secondLetter) != 0) { 1847 // special case to ignore 'et al' 1848 if (strCharCmp("ET", s->mWords[s->mWordCount - 2], 2, 1) == false) { 1849 s->mStateWord = s->mWordCount - 1; 1850 s->mZipHint = firstIndex[firstLetter] + 1851 bitcount(stateTwoLetter[firstLetter] & ((1 << secondLetter) - 1)); 1852 goto foundStateName; 1853 } 1854 } 1855 goto resetWord; 1856 } 1857 s->mStates = longStateNames[firstLetter]; 1858 if (s->mStates == NULL) 1859 goto resetWord; 1860 mustBeAllUpper = false; 1861 s->mProgress = STATE_NAME; 1862 unsigned char section = s->mStates[0]; 1863 ASSERT(section > 0x80); 1864 s->mSectionLength = section & 0x7f; 1865 candIndex = 1; 1866 secondHalf = false; 1867 s->mStateWord = s->mWordCount - 1; 1868 goto stateName; 1869 } 1870 if (WTF::isASCIIDigit(ch)) { 1871 if (s->mLetterCount == 0) { 1872 if (++s->mNumberCount > 1) 1873 continue; 1874 if (s->mWordCount == 0 && s->mContinuationNode) 1875 return FOUND_NONE; 1876 s->newWord(baseChars, chars); 1877 s->mNumberWords |= 1 << s->mWordCount; 1878 s->mUnparsed = true; 1879 } 1880 continue; 1881 } 1882 if (ch == '.') { // optionally can follow letters 1883 if (s->mLetterCount == 0) 1884 break; 1885 if (s->mNumberCount > 0) 1886 break; 1887 continue; 1888 } 1889 if (ch == '/') // between numbers (1/2) between words (12 Main / Ste 4d) 1890 goto resetWord; 1891 if (ch == '#') // can precede numbers, allow it to appear randomly 1892 goto resetWord; 1893 if (ch == '"') // sometimes parts of addresses are quoted (FIXME: cite an example here) 1894 continue; 1895 break; 1896 case SECOND_HALF: 1897 if (WTF::isASCIIAlpha(ch)) { 1898 if (s->mLetterCount == 0) { 1899 s->newWord(baseChars, chars); 1900 s->mWordCount++; 1901 } 1902 s->mLetterCount++; 1903 continue; 1904 } 1905 if (WTF::isASCIIDigit(ch) == false) { 1906 if (s->mLetterCount > 0) { 1907 s->mProgress = STATE_NAME; 1908 candIndex = 0; 1909 secondHalf = true; 1910 goto stateName; 1911 } 1912 continue; 1913 } 1914 s->mProgress = ADDRESS_LINE; 1915 goto resetState; 1916 case STATE_NAME: 1917 stateName: 1918 // pick up length of first section 1919 do { 1920 int stateIndex = 1; 1921 int skip = 0; 1922 int prefix = 0; 1923 bool subStr = false; 1924 do { 1925 unsigned char match = s->mStates[stateIndex]; 1926 if (match >= 0x80) { 1927 if (stateIndex == s->mSectionLength) 1928 break; 1929 subStr = true; 1930 // if (skip > 0) 1931 // goto foundStateName; 1932 prefix = candIndex; 1933 skip = match & 0x7f; 1934 match = s->mStates[++stateIndex]; 1935 } 1936 UChar candChar = s->mWords[s->mWordCount - 1][candIndex]; 1937 if (mustBeAllUpper && WTF::isASCIILower(candChar)) 1938 goto skipToNext; 1939 if (match != candChar) { 1940 if (match != WTF::toASCIILower(candChar)) { 1941 skipToNext: 1942 if (subStr == false) 1943 break; 1944 if (stateIndex == s->mSectionLength) { 1945 if (secondHalf) { 1946 s->mProgress = ADDRESS_LINE; 1947 goto resetState; 1948 } 1949 break; 1950 } 1951 stateIndex += skip; 1952 skip = 0; 1953 candIndex = prefix; 1954 continue; // try next substring 1955 } 1956 mustBeAllUpper = true; 1957 } 1958 int nextindex = stateIndex + 1; 1959 if (++candIndex >= s->mLetterCount && s->mStates[nextindex] == ' ') { 1960 s->mProgress = SECOND_HALF; 1961 s->mStates += nextindex; 1962 s->mSectionLength -= nextindex; 1963 goto resetWord; 1964 } 1965 if (nextindex + 1 == s->mSectionLength || skip == 2) { 1966 s->mZipHint = s->mStates[nextindex] - 1; 1967 goto foundStateName; 1968 } 1969 stateIndex += 1; 1970 skip -= 1; 1971 } while (true); 1972 s->mStates += s->mSectionLength; 1973 ASSERT(s->mStates[0] == 0 || (unsigned) s->mStates[0] > 0x80); 1974 s->mSectionLength = s->mStates[0] & 0x7f; 1975 candIndex = 1; 1976 subStr = false; 1977 } while (s->mSectionLength != 0); 1978 s->mProgress = ADDRESS_LINE; 1979 goto resetState; 1980 foundStateName: 1981 s->mEndResult = chars - s->mCurrentStart; 1982 s->mEndWord = s->mWordCount - 1; 1983 s->mProgress = ZIP_CODE; 1984 // a couple of delimiters is an indication that the state name is good 1985 // or, a non-space / non-alpha-digit is also good 1986 s->mZipDelimiter = s->mLineCount > 2 1987 || isUnicodeSpace(ch) == false 1988 || chars == s->mEnd; 1989 if (WTF::isASCIIDigit(ch)) 1990 s->mZipStart = chars; 1991 goto resetState; 1992 case ZIP_CODE: 1993 if (WTF::isASCIIDigit(ch)) { 1994 int count = ++s->mNumberCount; 1995 if (count == 1) { 1996 if (WTF::isASCIIDigit(prior)) 1997 ++s->mNumberCount; 1998 else 1999 s->mZipStart = chars; 2000 } 2001 if (count <= 9) 2002 continue; 2003 } else if (isUnicodeSpace(ch)) { 2004 if (s->mNumberCount == 0) { 2005 s->mZipDelimiter = true; // two spaces delimit state name 2006 continue; 2007 } 2008 } else if (ch == '-') { 2009 if (s->mNumberCount == 5 && validZip(s->mZipHint, s->mZipStart)) { 2010 s->mNumberCount = 0; 2011 s->mProgress = PLUS_4; 2012 continue; 2013 } 2014 if (s->mNumberCount == 0) 2015 s->mZipDelimiter = true; 2016 } else if (WTF::isASCIIAlpha(ch) == false) 2017 s->mZipDelimiter = true; 2018 else { 2019 if (s->mLetterCount == 0) { 2020 s->newWord(baseChars, chars); 2021 s->mUnparsed = true; 2022 } 2023 ++s->mLetterCount; 2024 } 2025 if (s->mNumberCount == 5 || s->mNumberCount == 9) { 2026 if (validZip(s->mZipHint, s->mZipStart) == false) 2027 goto noZipMatch; 2028 s->mEndResult = chars - s->mCurrentStart; 2029 s->mEndWord = s->mWordCount - 1; 2030 } else if (s->mZipDelimiter == false) { 2031 noZipMatch: 2032 --chars; 2033 s->mProgress = ADDRESS_LINE; 2034 continue; 2035 } 2036 s->mProgress = FIND_STREET; 2037 goto findStreet; 2038 case PLUS_4: 2039 if (WTF::isASCIIDigit(ch)) { 2040 if (++s->mNumberCount <= 4) 2041 continue; 2042 } 2043 if (isUnicodeSpace(ch)) { 2044 if (s->mNumberCount == 0) 2045 continue; 2046 } 2047 if (s->mNumberCount == 4) { 2048 if (WTF::isASCIIAlpha(ch) == false) { 2049 s->mEndResult = chars - s->mCurrentStart; 2050 s->mEndWord = s->mWordCount - 1; 2051 } 2052 } else if (s->mNumberCount != 0) 2053 break; 2054 s->mProgress = FIND_STREET; 2055 case FIND_STREET: 2056 findStreet: 2057 retryState = false; 2058 for (int wordsIndex = s->mStateWord - 1; wordsIndex >= 0; --wordsIndex) { 2059 const UChar* test = s->mWords[wordsIndex]; 2060 UChar letter = test[0]; 2061 letter -= 'A'; 2062 if (letter > 'X' - 'A') 2063 continue; 2064 const char* names = longStreetNames[letter]; 2065 if (names == NULL) 2066 continue; 2067 int offset; 2068 while ((offset = *names++) != 0) { 2069 int testIndex = 1; 2070 bool abbr = false; 2071 for (int idx = 0; idx < offset; idx++) { 2072 char nameLetter = names[idx]; 2073 char testUpper = WTF::toASCIIUpper(test[testIndex]); 2074 if (nameLetter == '*') { 2075 if (testUpper == 'S') 2076 testIndex++; 2077 break; 2078 } 2079 bool fullOnly = WTF::isASCIILower(nameLetter); 2080 nameLetter = WTF::toASCIIUpper(nameLetter); 2081 if (testUpper == nameLetter) { 2082 if (abbr && fullOnly) 2083 goto nextTest; 2084 testIndex++; 2085 continue; 2086 } 2087 if (fullOnly == false) 2088 goto nextTest; 2089 abbr = true; 2090 } 2091 letter = &test[testIndex] < s->mEnds[wordsIndex] ? 2092 test[testIndex] : ' '; 2093 if (WTF::isASCIIAlpha(letter) == false && WTF::isASCIIDigit(letter) == false) { 2094 if (s->mNumberWords != 0) { 2095 int shift = 0; 2096 int wordReduction = -1; 2097 do { 2098 while ((s->mNumberWords & (1 << shift)) == 0) 2099 shift++; 2100 if (shift > wordsIndex) 2101 break; 2102 wordReduction = shift; 2103 } while (s->mNumberWords >> ++shift != 0); 2104 if (wordReduction >= 0) { 2105 if (s->mContinuationNode) { 2106 if (retryState) 2107 break; 2108 return FOUND_NONE; 2109 } 2110 s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction]; 2111 } 2112 } 2113 if (wordsIndex != s->mStateWord - 1) 2114 return FOUND_COMPLETE; 2115 retryState = true; 2116 } 2117 nextTest: 2118 names += offset; 2119 } 2120 } 2121 if (retryState) { 2122 s->mProgress = ADDRESS_LINE; 2123 s->mStates = NULL; 2124 continue; 2125 } 2126 if (s->mNumberWords != 0) { 2127 unsigned shift = 0; 2128 while ((s->mNumberWords & (1 << shift)) == 0) 2129 shift++; 2130 s->mNumberWords >>= ++shift; 2131 if (s->mBases[0] != s->mBases[shift]) 2132 return FOUND_NONE; 2133 s->shiftWords(shift); 2134 s->mStartResult = s->mWords[0] - s->mStarts[0]; 2135 s->mWordCount -= shift; 2136 s->mProgress = ADDRESS_LINE; 2137 --chars; 2138 continue; 2139 } 2140 break; 2141 } 2142 if (s->mContinuationNode) 2143 return FOUND_NONE; 2144 s->mProgress = NO_ADDRESS; 2145 s->mWordCount = s->mLineCount = 0; 2146 s->mNumberWords = 0; 2147 resetState: 2148 s->mStates = NULL; 2149 resetWord: 2150 s->mNumberCount = s->mLetterCount = 0; 2151 s->mFirstLower = NULL; 2152 s->mUnparsed = false; 2153 } 2154 s->mCurrent = ch; 2155 return s->mProgress == NO_ADDRESS ? FOUND_NONE : FOUND_PARTIAL; 2156 } 2157 2158 // Recogize common email patterns only. Currently has lots of state, walks text forwards and backwards -- will be 2159 // a real challenge to adapt to walk text across multiple nodes, I imagine 2160 // FIXME: it's too hard for the caller to call these incrementally -- it's probably best for this to 2161 // either walk the node tree directly or make a callout to get the next or previous node, if there is one 2162 // walking directly will avoid adding logic in caller to track the multiple partial or full nodes that compose this 2163 // text pattern. 2164 CacheBuilder::FoundState CacheBuilder::FindPartialEMail(const UChar* chars, unsigned length, 2165 FindState* s) 2166 { 2167 // the following tables were generated by tests/browser/focusNavigation/BrowserDebug.cpp 2168 // hand-edit at your own risk 2169 static const int domainTwoLetter[] = { 2170 0x02df797c, // a followed by: [cdefgilmnoqrstuwxz] 2171 0x036e73fb, // b followed by: [abdefghijmnorstvwyz] 2172 0x03b67ded, // c followed by: [acdfghiklmnorsuvxyz] 2173 0x02005610, // d followed by: [ejkmoz] 2174 0x001e00d4, // e followed by: [ceghrstu] 2175 0x00025700, // f followed by: [ijkmor] 2176 0x015fb9fb, // g followed by: [abdefghilmnpqrstuwy] 2177 0x001a3400, // h followed by: [kmnrtu] 2178 0x000f7818, // i followed by: [delmnoqrst] 2179 0x0000d010, // j followed by: [emop] 2180 0x0342b1d0, // k followed by: [eghimnprwyz] 2181 0x013e0507, // l followed by: [abcikrstuvy] 2182 0x03fffccd, // m followed by: [acdghklmnopqrstuvwxyz] 2183 0x0212c975, // n followed by: [acefgilopruz] 2184 0x00001000, // o followed by: [m] 2185 0x014e3cf1, // p followed by: [aefghklmnrstwy] 2186 0x00000001, // q followed by: [a] 2187 0x00504010, // r followed by: [eouw] 2188 0x032a7fdf, // s followed by: [abcdeghijklmnortvyz] 2189 0x026afeec, // t followed by: [cdfghjklmnoprtvwz] 2190 0x03041441, // u followed by: [agkmsyz] 2191 0x00102155, // v followed by: [aceginu] 2192 0x00040020, // w followed by: [fs] 2193 0x00000000, // x 2194 0x00180010, // y followed by: [etu] 2195 0x00401001, // z followed by: [amw] 2196 }; 2197 2198 static char const* const longDomainNames[] = { 2199 "\x03" "ero" "\x03" "rpa", // aero, arpa 2200 "\x02" "iz", // biz 2201 "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop 2202 NULL, // d 2203 "\x02" "du", // edu 2204 NULL, // f 2205 "\x02" "ov", // gov 2206 NULL, // h 2207 "\x03" "nfo" "\x02" "nt", // info, int 2208 "\x03" "obs", // jobs 2209 NULL, // k 2210 NULL, // l 2211 "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum 2212 "\x03" "ame" "\x02" "et", // name, net 2213 "\x02" "rg", // , org 2214 "\x02" "ro", // pro 2215 NULL, // q 2216 NULL, // r 2217 NULL, // s 2218 "\x05" "ravel", // travel 2219 NULL, // u 2220 NULL, // v 2221 NULL, // w 2222 NULL, // x 2223 NULL, // y 2224 NULL, // z 2225 }; 2226 2227 const UChar* start = chars; 2228 const UChar* end = chars + length; 2229 while (chars < end) { 2230 UChar ch = *chars++; 2231 if (ch != '@') 2232 continue; 2233 const UChar* atLocation = chars - 1; 2234 // search for domain 2235 ch = *chars++ | 0x20; // convert uppercase to lower 2236 if (ch < 'a' || ch > 'z') 2237 continue; 2238 while (chars < end) { 2239 ch = *chars++; 2240 if (IsDomainChar(ch) == false) 2241 goto nextAt; 2242 if (ch != '.') 2243 continue; 2244 UChar firstLetter = *chars++ | 0x20; // first letter of the domain 2245 if (chars >= end) 2246 return FOUND_NONE; // only one letter; must be at least two 2247 firstLetter -= 'a'; 2248 if (firstLetter > 'z' - 'a') 2249 continue; // non-letter followed '.' 2250 int secondLetterMask = domainTwoLetter[firstLetter]; 2251 ch = *chars | 0x20; // second letter of the domain 2252 ch -= 'a'; 2253 if (ch >= 'z' - 'a') 2254 continue; 2255 bool secondMatch = (secondLetterMask & 1 << ch) != 0; 2256 const char* wordMatch = longDomainNames[firstLetter]; 2257 int wordIndex = 0; 2258 while (wordMatch != NULL) { 2259 int len = *wordMatch++; 2260 char match; 2261 do { 2262 match = wordMatch[wordIndex]; 2263 if (match < 0x20) 2264 goto foundDomainStart; 2265 if (chars[wordIndex] != match) 2266 break; 2267 wordIndex++; 2268 } while (true); 2269 wordMatch += len; 2270 if (*wordMatch == '\0') 2271 break; 2272 wordIndex = 0; 2273 } 2274 if (secondMatch) { 2275 wordIndex = 1; 2276 foundDomainStart: 2277 chars += wordIndex; 2278 if (chars < end) { 2279 ch = *chars; 2280 if (ch != '.') { 2281 if (IsDomainChar(ch)) 2282 goto nextDot; 2283 } else if (chars + 1 < end && IsDomainChar(chars[1])) 2284 goto nextDot; 2285 } 2286 // found domain. Search backwards from '@' for beginning of email address 2287 s->mEndResult = chars - start; 2288 chars = atLocation; 2289 if (chars <= start) 2290 goto nextAt; 2291 ch = *--chars; 2292 if (ch == '.') 2293 goto nextAt; // mailbox can't end in period 2294 do { 2295 if (IsMailboxChar(ch) == false) { 2296 chars++; 2297 break; 2298 } 2299 if (chars == start) 2300 break; 2301 ch = *--chars; 2302 } while (true); 2303 UChar firstChar = *chars; 2304 if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty 2305 goto nextAt; 2306 s->mStartResult = chars - start; 2307 return FOUND_COMPLETE; 2308 } 2309 nextDot: 2310 ; 2311 } 2312 nextAt: 2313 chars = atLocation + 1; 2314 } 2315 return FOUND_NONE; 2316 } 2317 2318 #define PHONE_PATTERN "(200) /-.\\ 100 -. 0000" // poor man's regex: parens optional, any one of punct, digit smallest allowed 2319 2320 CacheBuilder::FoundState CacheBuilder::FindPartialNumber(const UChar* chars, unsigned length, 2321 FindState* s) 2322 { 2323 char* pattern = s->mPattern; 2324 UChar* store = s->mStorePtr; 2325 const UChar* start = chars; 2326 const UChar* end = chars + length; 2327 const UChar* lastDigit = NULL; 2328 do { 2329 bool initialized = s->mInitialized; 2330 while (chars < end) { 2331 if (initialized == false) { 2332 s->mBackTwo = s->mBackOne; 2333 s->mBackOne = s->mCurrent; 2334 } 2335 UChar ch = s->mCurrent = *chars; 2336 do { 2337 char patternChar = *pattern; 2338 switch (patternChar) { 2339 case '2': 2340 if (initialized == false) { 2341 s->mStartResult = chars - start; 2342 initialized = true; 2343 } 2344 case '0': 2345 case '1': 2346 if (ch < patternChar || ch > '9') 2347 goto resetPattern; 2348 *store++ = ch; 2349 pattern++; 2350 lastDigit = chars; 2351 goto nextChar; 2352 case '\0': 2353 if (WTF::isASCIIDigit(ch) == false) { 2354 *store = '\0'; 2355 goto checkMatch; 2356 } 2357 goto resetPattern; 2358 case ' ': 2359 if (ch == patternChar) 2360 goto nextChar; 2361 break; 2362 case '(': 2363 if (ch == patternChar) { 2364 s->mStartResult = chars - start; 2365 initialized = true; 2366 s->mOpenParen = true; 2367 } 2368 goto commonPunctuation; 2369 case ')': 2370 if ((ch == patternChar) ^ s->mOpenParen) 2371 goto resetPattern; 2372 default: 2373 commonPunctuation: 2374 if (ch == patternChar) { 2375 pattern++; 2376 goto nextChar; 2377 } 2378 } 2379 } while (++pattern); // never false 2380 nextChar: 2381 chars++; 2382 } 2383 break; 2384 resetPattern: 2385 if (s->mContinuationNode) 2386 return FOUND_NONE; 2387 FindResetNumber(s); 2388 pattern = s->mPattern; 2389 store = s->mStorePtr; 2390 } while (++chars < end); 2391 checkMatch: 2392 if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo)) 2393 return FOUND_NONE; 2394 *store = '\0'; 2395 s->mStorePtr = store; 2396 s->mPattern = pattern; 2397 s->mEndResult = lastDigit - start + 1; 2398 char pState = pattern[0]; 2399 return pState == '\0' ? FOUND_COMPLETE : pState == '(' || (WTF::isASCIIDigit(pState) && WTF::isASCIIDigit(pattern[-1])) ? 2400 FOUND_NONE : FOUND_PARTIAL; 2401 } 2402 2403 CacheBuilder::FoundState CacheBuilder::FindPhoneNumber(const UChar* chars, unsigned length, 2404 int* start, int* end) 2405 { 2406 FindState state; 2407 FindReset(&state); 2408 FoundState result = FindPartialNumber(chars, length, &state); 2409 *start = state.mStartResult; 2410 *end = state.mEndResult; 2411 return result; 2412 } 2413 2414 void CacheBuilder::FindReset(FindState* state) 2415 { 2416 memset(state, 0, sizeof(FindState)); 2417 state->mCurrent = ' '; 2418 FindResetNumber(state); 2419 } 2420 2421 void CacheBuilder::FindResetNumber(FindState* state) 2422 { 2423 state->mOpenParen = false; 2424 state->mPattern = (char*) PHONE_PATTERN; 2425 state->mStorePtr = state->mStore; 2426 } 2427 2428 IntRect CacheBuilder::getAreaRect(const HTMLAreaElement* area) 2429 { 2430 Node* node = area->document(); 2431 while ((node = node->traverseNextNode()) != NULL) { 2432 RenderObject* renderer = node->renderer(); 2433 if (renderer && renderer->isRenderImage()) { 2434 RenderImage* image = static_cast<RenderImage*>(renderer); 2435 HTMLMapElement* map = image->imageMap(); 2436 if (map) { 2437 Node* n; 2438 for (n = map->firstChild(); n; 2439 n = n->traverseNextNode(map)) { 2440 if (n == area) { 2441 if (area->isDefault()) 2442 return image->absoluteBoundingBoxRect(); 2443 return area->getRect(image); 2444 } 2445 } 2446 } 2447 } 2448 } 2449 return IntRect(); 2450 } 2451 2452 void CacheBuilder::GetGlobalOffset(Node* node, int* x, int * y) 2453 { 2454 GetGlobalOffset(node->document()->frame(), x, y); 2455 } 2456 2457 void CacheBuilder::GetGlobalOffset(Frame* frame, int* x, int* y) 2458 { 2459 // TIMER_PROBE(__FUNCTION__); 2460 ASSERT(x); 2461 ASSERT(y); 2462 *x = 0; 2463 *y = 0; 2464 if (!frame->view()) 2465 return; 2466 Frame* parent; 2467 while ((parent = frame->tree()->parent()) != NULL) { 2468 const WebCore::IntRect& rect = frame->view()->platformWidget()->getBounds(); 2469 *x += rect.x(); 2470 *y += rect.y(); 2471 frame = parent; 2472 } 2473 // TIMER_PROBE_END(); 2474 } 2475 2476 Frame* CacheBuilder::HasFrame(Node* node) 2477 { 2478 RenderObject* renderer = node->renderer(); 2479 if (renderer == NULL) 2480 return NULL; 2481 if (renderer->isWidget() == false) 2482 return NULL; 2483 Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); 2484 if (widget == NULL) 2485 return NULL; 2486 if (widget->isFrameView() == false) 2487 return NULL; 2488 return static_cast<FrameView*>(widget)->frame(); 2489 } 2490 2491 bool CacheBuilder::HasOverOrOut(Node* node) 2492 { 2493 // eventNames are thread-local data, I avoid using 'static' variable here. 2494 AtomicString eventTypes[2] = { 2495 eventNames().mouseoverEvent, 2496 eventNames().mouseoutEvent 2497 }; 2498 2499 return NodeHasEventListeners(node, eventTypes, 2); 2500 } 2501 2502 bool CacheBuilder::HasTriggerEvent(Node* node) 2503 { 2504 AtomicString eventTypes[5] = { 2505 eventNames().clickEvent, 2506 eventNames().mousedownEvent, 2507 eventNames().mouseupEvent, 2508 eventNames().keydownEvent, 2509 eventNames().keyupEvent 2510 }; 2511 2512 return NodeHasEventListeners(node, eventTypes, 5); 2513 } 2514 2515 // #define EMAIL_PATTERN "x (at) y.d" // where 'x' is letters, numbers, and '-', '.', '_' ; 'y' is 'x' without the underscore, and 'd' is a valid domain 2516 // - 0x2D . 0x2E 0-9 0x30-39 A-Z 0x41-5A _ 0x5F a-z 0x61-7A 2517 2518 bool CacheBuilder::IsDomainChar(UChar ch) 2519 { 2520 static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z 2521 ch -= 0x20; 2522 if (ch > 'z' - 0x20) 2523 return false; 2524 return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; 2525 } 2526 2527 bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node, 2528 CachedNodeType* type, String* exported) const 2529 { 2530 Text* textNode = static_cast<Text*>(node); 2531 StringImpl* string = textNode->dataImpl(); 2532 const UChar* baseChars = string->characters(); 2533 // const UChar* originalBase = baseChars; 2534 int length = string->length(); 2535 int index = 0; 2536 while (index < length && isUnicodeSpace(baseChars[index])) 2537 index++; 2538 if (index >= length) 2539 return false; 2540 if (more == false) { 2541 walk->mStart = 0; 2542 walk->mEnd = 0; 2543 walk->mFinalNode = node; 2544 walk->mLastInline = NULL; 2545 } 2546 // starting with this node, search forward for email, phone number, and address 2547 // if any of the three is found, track it so that the remaining can be looked for later 2548 FoundState state = FOUND_NONE; 2549 RenderText* renderer = (RenderText*) node->renderer(); 2550 bool foundBetter = false; 2551 InlineTextBox* baseInline = walk->mLastInline != NULL ? walk->mLastInline : 2552 renderer->firstTextBox(); 2553 if (baseInline == NULL) 2554 return false; 2555 int start = walk->mEnd; 2556 InlineTextBox* saveInline; 2557 int baseStart, firstStart = start; 2558 saveInline = baseInline; 2559 baseStart = start; 2560 for (CachedNodeType checkType = ADDRESS_CACHEDNODETYPE; 2561 checkType <= PHONE_CACHEDNODETYPE; 2562 checkType = static_cast<CachedNodeType>(checkType + 1)) 2563 { 2564 if ((1 << (checkType - 1) & mAllowableTypes) == 0) 2565 continue; 2566 InlineTextBox* inlineTextBox = baseInline; 2567 FindState findState; 2568 FindReset(&findState); 2569 start = baseStart; 2570 if (checkType == ADDRESS_CACHEDNODETYPE) { 2571 findState.mBases[0] = baseChars; 2572 findState.mWords[0] = baseChars + start; 2573 findState.mStarts[0] = baseChars + start; 2574 } 2575 Node* lastPartialNode = NULL; 2576 int lastPartialEnd = -1; 2577 bool lastPartialMore = false; 2578 bool firstPartial = true; 2579 InlineTextBox* lastPartialInline = NULL; 2580 do { 2581 do { 2582 const UChar* chars = baseChars + start; 2583 length = inlineTextBox == NULL ? 0 : 2584 inlineTextBox->end() - start + 1; 2585 bool wasInitialized = findState.mInitialized; 2586 switch (checkType) { 2587 case ADDRESS_CACHEDNODETYPE: 2588 state = FindPartialAddress(baseChars, chars, length, &findState); 2589 break; 2590 case EMAIL_CACHEDNODETYPE: 2591 state = FindPartialEMail(chars, length, &findState); 2592 break; 2593 case PHONE_CACHEDNODETYPE: 2594 state = FindPartialNumber(chars, length, &findState); 2595 break; 2596 default: 2597 ASSERT(0); 2598 } 2599 findState.mInitialized = state != FOUND_NONE; 2600 if (wasInitialized != findState.mInitialized) 2601 firstStart = start; 2602 if (state == FOUND_PARTIAL) { 2603 lastPartialNode = node; 2604 lastPartialEnd = findState.mEndResult + start; 2605 lastPartialMore = firstPartial && 2606 lastPartialEnd < (int) string->length(); 2607 firstPartial = false; 2608 lastPartialInline = inlineTextBox; 2609 findState.mContinuationNode = true; 2610 } else if (state == FOUND_COMPLETE) { 2611 if (foundBetter == false || walk->mStart > findState.mStartResult) { 2612 walk->mStart = findState.mStartResult + firstStart; 2613 if (findState.mEndResult > 0) { 2614 walk->mFinalNode = node; 2615 walk->mEnd = findState.mEndResult + start; 2616 walk->mMore = node == textNode && 2617 walk->mEnd < (int) string->length(); 2618 walk->mLastInline = inlineTextBox; 2619 } else { 2620 walk->mFinalNode = lastPartialNode; 2621 walk->mEnd = lastPartialEnd; 2622 walk->mMore = lastPartialMore; 2623 walk->mLastInline = lastPartialInline; 2624 } 2625 *type = checkType; 2626 if (checkType == PHONE_CACHEDNODETYPE) { 2627 const UChar* store = findState.mStore; 2628 *exported = String(store); 2629 } else { 2630 Node* temp = textNode; 2631 length = 1; 2632 start = walk->mStart; 2633 exported->truncate(0); 2634 do { 2635 Text* tempText = static_cast<Text*>(temp); 2636 StringImpl* string = tempText->dataImpl(); 2637 int end = tempText == walk->mFinalNode ? 2638 walk->mEnd : string->length(); 2639 exported->append(String(string->substring( 2640 start, end - start))); 2641 ASSERT(end > start); 2642 length += end - start + 1; 2643 if (temp == walk->mFinalNode) 2644 break; 2645 start = 0; 2646 do { 2647 temp = temp->traverseNextNode(); 2648 ASSERT(temp); 2649 } while (temp->isTextNode() == false); 2650 // add a space in between text nodes to avoid 2651 // words collapsing together 2652 exported->append(" "); 2653 } while (true); 2654 } 2655 foundBetter = true; 2656 } 2657 goto tryNextCheckType; 2658 } else if (findState.mContinuationNode) 2659 break; 2660 if (inlineTextBox == NULL) 2661 break; 2662 inlineTextBox = inlineTextBox->nextTextBox(); 2663 if (inlineTextBox == NULL) 2664 break; 2665 start = inlineTextBox->start(); 2666 if (state == FOUND_PARTIAL && node == textNode) 2667 findState.mContinuationNode = false; 2668 } while (true); 2669 if (state == FOUND_NONE) 2670 break; 2671 // search for next text node, if any 2672 Text* nextNode; 2673 do { 2674 do { 2675 do { 2676 node = node->traverseNextNode(); 2677 if (node == NULL || node->hasTagName(HTMLNames::aTag) 2678 || node->hasTagName(HTMLNames::inputTag) 2679 || node->hasTagName(HTMLNames::textareaTag)) { 2680 if (state == FOUND_PARTIAL && 2681 checkType == ADDRESS_CACHEDNODETYPE && 2682 findState.mProgress == ZIP_CODE && 2683 findState.mNumberCount == 0) { 2684 baseChars = NULL; 2685 inlineTextBox = NULL; 2686 start = 0; 2687 findState.mProgress = FIND_STREET; 2688 goto finalNode; 2689 } 2690 goto tryNextCheckType; 2691 } 2692 } while (node->isTextNode() == false); 2693 nextNode = static_cast<Text*>(node); 2694 renderer = (RenderText*) nextNode->renderer(); 2695 } while (renderer == NULL); 2696 baseInline = renderer->firstTextBox(); 2697 } while (baseInline == NULL); 2698 string = nextNode->dataImpl(); 2699 baseChars = string->characters(); 2700 inlineTextBox = baseInline; 2701 start = inlineTextBox->start(); 2702 finalNode: 2703 findState.mEndResult = 0; 2704 } while (true); 2705 tryNextCheckType: 2706 node = textNode; 2707 baseInline = saveInline; 2708 string = textNode->dataImpl(); 2709 baseChars = string->characters(); 2710 } 2711 if (foundBetter) { 2712 CachedNodeType temp = *type; 2713 switch (temp) { 2714 case ADDRESS_CACHEDNODETYPE: { 2715 static const char geoString[] = "geo:0,0?q="; 2716 exported->insert(String(geoString), 0); 2717 int index = sizeof(geoString) - 1; 2718 String escapedComma("%2C"); 2719 while ((index = exported->find(',', index)) >= 0) 2720 exported->replace(index, 1, escapedComma); 2721 } break; 2722 case EMAIL_CACHEDNODETYPE: 2723 exported->insert(WebCore::String("mailto:"), 0); 2724 break; 2725 case PHONE_CACHEDNODETYPE: 2726 exported->insert(WebCore::String("tel:"), 0); 2727 break; 2728 default: 2729 break; 2730 } 2731 return true; 2732 } 2733 noTextMatch: 2734 walk->reset(); 2735 return false; 2736 } 2737 2738 bool CacheBuilder::IsMailboxChar(UChar ch) 2739 { 2740 static const unsigned body[] = {0x03ff6000, 0x87fffffe, 0x07fffffe}; // 0-9 . - A-Z _ a-z 2741 ch -= 0x20; 2742 if (ch > 'z' - 0x20) 2743 return false; 2744 return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; 2745 } 2746 2747 bool CacheBuilder::setData(CachedFrame* cachedFrame) 2748 { 2749 Frame* frame = FrameAnd(this); 2750 Document* doc = frame->document(); 2751 if (doc == NULL) 2752 return false; 2753 RenderObject* renderer = doc->renderer(); 2754 if (renderer == NULL) 2755 return false; 2756 RenderLayer* layer = renderer->enclosingLayer(); 2757 if (layer == NULL) 2758 return false; 2759 if (layer->width() == 0 || layer->height() == 0) 2760 return false; 2761 if (!frame->view()) 2762 return false; 2763 int x, y; 2764 GetGlobalOffset(frame, &x, &y); 2765 WebCore::IntRect viewBounds = frame->view()->platformWidget()->getBounds(); 2766 if ((x | y) != 0) 2767 viewBounds.setLocation(WebCore::IntPoint(x, y)); 2768 cachedFrame->setLocalViewBounds(viewBounds); 2769 cachedFrame->setContentsSize(layer->width(), layer->height()); 2770 if (cachedFrame->childCount() == 0) 2771 return true; 2772 CachedFrame* lastCachedFrame = cachedFrame->lastChild(); 2773 cachedFrame = cachedFrame->firstChild(); 2774 do { 2775 CacheBuilder* cacheBuilder = Builder((Frame* )cachedFrame->framePointer()); 2776 cacheBuilder->setData(cachedFrame); 2777 } while (cachedFrame++ != lastCachedFrame); 2778 return true; 2779 } 2780 2781 #if USE(ACCELERATED_COMPOSITING) 2782 void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker, 2783 RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY) 2784 { 2785 RenderLayer* layer = toRenderBoxModelObject(nodeRenderer)->layer(); 2786 RenderLayerBacking* back = layer->backing(); 2787 if (!back) 2788 return; 2789 GraphicsLayerAndroid* grLayer = static_cast 2790 <GraphicsLayerAndroid*>(back->graphicsLayer()); 2791 if (!grLayer) 2792 return; 2793 LayerAndroid* aLayer = grLayer->contentLayer(); 2794 if (!aLayer) 2795 return; 2796 layerTracker.grow(layerTracker.size() + 1); 2797 LayerTracker& indexTracker = layerTracker.last(); 2798 indexTracker.mLayer = aLayer; 2799 indexTracker.mBounds = nodeRenderer->absoluteBoundingBoxRect(); 2800 indexTracker.mBounds.move(offsetX, offsetY); 2801 indexTracker.mLastChild = lastChild ? OneAfter(lastChild) : 0; 2802 DBG_NAV_LOGD("layer=%p [%d] bounds=(%d,%d,w=%d,h=%d)", aLayer, 2803 aLayer->uniqueId(), indexTracker.mBounds.x(), indexTracker.mBounds.y(), 2804 indexTracker.mBounds.width(), indexTracker.mBounds.height()); 2805 } 2806 #endif 2807 2808 bool CacheBuilder::validNode(Frame* startFrame, void* matchFrame, 2809 void* matchNode) 2810 { 2811 if (matchFrame == startFrame) { 2812 if (matchNode == NULL) 2813 return true; 2814 Node* node = startFrame->document(); 2815 while (node != NULL) { 2816 if (node == matchNode) { 2817 const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ? 2818 getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect(); 2819 // Consider nodes with empty rects that are not at the origin 2820 // to be valid, since news.google.com has valid nodes like this 2821 if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty()) 2822 return false; 2823 return true; 2824 } 2825 node = node->traverseNextNode(); 2826 } 2827 DBG_NAV_LOGD("frame=%p valid node=%p invalid\n", matchFrame, matchNode); 2828 return false; 2829 } 2830 Frame* child = startFrame->tree()->firstChild(); 2831 while (child) { 2832 bool result = validNode(child, matchFrame, matchNode); 2833 if (result) 2834 return result; 2835 child = child->tree()->nextSibling(); 2836 } 2837 #if DEBUG_NAV_UI 2838 if (startFrame->tree()->parent() == NULL) 2839 DBG_NAV_LOGD("frame=%p node=%p false\n", matchFrame, matchNode); 2840 #endif 2841 return false; 2842 } 2843 2844 static int Area(const IntRect& rect) 2845 { 2846 return rect.width() * rect.height(); 2847 } 2848 2849 bool CacheBuilder::AddPartRect(IntRect& bounds, int x, int y, 2850 WTF::Vector<IntRect>* result, IntRect* focusBounds) 2851 { 2852 if (bounds.isEmpty()) 2853 return true; 2854 bounds.move(x, y); 2855 if (bounds.right() <= 0 || bounds.bottom() <= 0) 2856 return true; 2857 IntRect* work = result->begin() - 1; 2858 IntRect* end = result->end(); 2859 while (++work < end) { 2860 if (work->contains(bounds)) 2861 return true; 2862 if (bounds.contains(*work)) { 2863 *work = bounds; 2864 focusBounds->unite(bounds); 2865 return true; 2866 } 2867 if ((bounds.x() != work->x() || bounds.width() != work->width()) && 2868 (bounds.y() != work->y() || bounds.height() != work->height())) 2869 continue; 2870 IntRect test = *work; 2871 test.unite(bounds); 2872 if (Area(test) > Area(*work) + Area(bounds)) 2873 continue; 2874 *work = test; 2875 focusBounds->unite(bounds); 2876 return true; 2877 } 2878 if (result->size() >= MAXIMUM_FOCUS_RING_COUNT) 2879 return false; 2880 result->append(bounds); 2881 if (focusBounds->isEmpty()) 2882 *focusBounds = bounds; 2883 else 2884 focusBounds->unite(bounds); 2885 return true; 2886 } 2887 2888 bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds, 2889 IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result) 2890 { 2891 WTF::Vector<ClipColumnTracker> clipTracker(1); 2892 ClipColumnTracker* baseTracker = clipTracker.data(); // sentinel 2893 bzero(baseTracker, sizeof(ClipColumnTracker)); 2894 if (node->hasChildNodes() && node->hasTagName(HTMLNames::buttonTag) == false 2895 && node->hasTagName(HTMLNames::selectTag) == false) { 2896 // collect all text rects from first to last child 2897 Node* test = node->firstChild(); 2898 Node* last = NULL; 2899 Node* prior = node; 2900 while ((prior = prior->lastChild()) != NULL) 2901 last = prior; 2902 ASSERT(last != NULL); 2903 bool nodeIsAnchor = node->hasTagName(HTMLNames::aTag); 2904 do { 2905 do { 2906 const ClipColumnTracker* lastClip = &clipTracker.last(); 2907 if (test != lastClip->mLastChild) 2908 break; 2909 clipTracker.removeLast(); 2910 } while (true); 2911 RenderObject* renderer = test->renderer(); 2912 if (renderer == NULL) 2913 continue; 2914 EVisibility vis = renderer->style()->visibility(); 2915 if (vis == HIDDEN) 2916 continue; 2917 if (test->isTextNode()) { 2918 RenderText* renderText = (RenderText*) renderer; 2919 InlineTextBox *textBox = renderText->firstTextBox(); 2920 if (textBox == NULL) 2921 continue; 2922 bool hasClip = renderer->hasOverflowClip(); 2923 size_t clipIndex = clipTracker.size(); 2924 IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); 2925 if (hasClip || --clipIndex > 0) { 2926 clipBounds = hasClip ? renderer->absoluteBoundingBoxRect() : 2927 clipTracker.at(clipIndex).mBounds; // x, y fixup done by ConstructTextRect 2928 } 2929 if (ConstructTextRect((Text*) test, textBox, 0, INT_MAX, 2930 x, y, focusBounds, clipBounds, result) == false) { 2931 return false; 2932 } 2933 continue; 2934 } 2935 if (test->hasTagName(HTMLNames::imgTag)) { 2936 IntRect bounds = test->getRect(); 2937 if (AddPartRect(bounds, x, y, result, focusBounds) == false) 2938 return false; 2939 continue; 2940 } 2941 if (renderer->hasOverflowClip() == false) { 2942 if (nodeIsAnchor && test->hasTagName(HTMLNames::divTag)) { 2943 IntRect bounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by AddPartRect 2944 int left = bounds.x() + ((RenderBox*)renderer)->paddingLeft() 2945 + ((RenderBox*)renderer)->borderLeft(); 2946 int top = bounds.y() + ((RenderBox*)renderer)->paddingTop() 2947 + ((RenderBox*)renderer)->borderTop(); 2948 int right = bounds.right() - ((RenderBox*)renderer)->paddingRight() 2949 - ((RenderBox*)renderer)->borderRight(); 2950 int bottom = bounds.bottom() - ((RenderBox*)renderer)->paddingBottom() 2951 - ((RenderBox*)renderer)->borderBottom(); 2952 if (left >= right || top >= bottom) 2953 continue; 2954 bounds = IntRect(left, top, right - left, bottom - top); 2955 if (AddPartRect(bounds, x, y, result, focusBounds) == false) 2956 return false; 2957 } 2958 continue; 2959 } 2960 Node* lastChild = test->lastChild(); 2961 if (lastChild == NULL) 2962 continue; 2963 clipTracker.grow(clipTracker.size() + 1); 2964 ClipColumnTracker& clip = clipTracker.last(); 2965 clip.mBounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by ConstructTextRect 2966 clip.mLastChild = OneAfter(lastChild); 2967 clip.mNode = test; 2968 } while (test != last && (test = test->traverseNextNode()) != NULL); 2969 } 2970 if (result->size() == 0 || focusBounds->width() < MINIMUM_FOCUSABLE_WIDTH 2971 || focusBounds->height() < MINIMUM_FOCUSABLE_HEIGHT) { 2972 if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) 2973 return false; 2974 if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) 2975 return false; 2976 result->append(bounds); 2977 *focusBounds = bounds; 2978 } 2979 return true; 2980 } 2981 2982 static inline bool isNotSpace(UChar c) 2983 { 2984 return c <= 0xA0 ? isUnicodeSpace(c) == false : 2985 WTF::Unicode::direction(c) != WTF::Unicode::WhiteSpaceNeutral; 2986 } 2987 2988 bool CacheBuilder::ConstructTextRect(Text* textNode, 2989 InlineTextBox* textBox, int start, int relEnd, int x, int y, 2990 IntRect* focusBounds, const IntRect& clipBounds, WTF::Vector<IntRect>* result) 2991 { 2992 RenderText* renderText = (RenderText*) textNode->renderer(); 2993 EVisibility vis = renderText->style()->visibility(); 2994 StringImpl* string = textNode->dataImpl(); 2995 const UChar* chars = string->characters(); 2996 FloatPoint pt = renderText->localToAbsolute(); 2997 do { 2998 int textBoxStart = textBox->start(); 2999 int textBoxEnd = textBoxStart + textBox->len(); 3000 if (textBoxEnd <= start) 3001 continue; 3002 if (textBoxEnd > relEnd) 3003 textBoxEnd = relEnd; 3004 IntRect bounds = textBox->selectionRect((int) pt.x(), (int) pt.y(), 3005 start, textBoxEnd); 3006 bounds.intersect(clipBounds); 3007 if (bounds.isEmpty()) 3008 continue; 3009 bool drawable = false; 3010 for (int index = start; index < textBoxEnd; index++) 3011 if ((drawable |= isNotSpace(chars[index])) != false) 3012 break; 3013 if (drawable && vis != HIDDEN) { 3014 if (AddPartRect(bounds, x, y, result, focusBounds) == false) 3015 return false; 3016 } 3017 if (textBoxEnd == relEnd) 3018 break; 3019 } while ((textBox = textBox->nextTextBox()) != NULL); 3020 return true; 3021 } 3022 3023 bool CacheBuilder::ConstructTextRects(Text* node, int start, 3024 Text* last, int end, int x, int y, IntRect* focusBounds, 3025 const IntRect& clipBounds, WTF::Vector<IntRect>* result) 3026 { 3027 result->clear(); 3028 *focusBounds = IntRect(0, 0, 0, 0); 3029 do { 3030 RenderText* renderText = (RenderText*) node->renderer(); 3031 int relEnd = node == last ? end : renderText->textLength(); 3032 InlineTextBox *textBox = renderText->firstTextBox(); 3033 if (textBox != NULL) { 3034 do { 3035 if ((int) textBox->end() >= start) 3036 break; 3037 } while ((textBox = textBox->nextTextBox()) != NULL); 3038 if (textBox && ConstructTextRect(node, textBox, start, relEnd, 3039 x, y, focusBounds, clipBounds, result) == false) 3040 return false; 3041 } 3042 start = 0; 3043 do { 3044 if (node == last) 3045 return true; 3046 node = (Text*) node->traverseNextNode(); 3047 ASSERT(node != NULL); 3048 } while (node->isTextNode() == false || node->renderer() == NULL); 3049 } while (true); 3050 } 3051 3052 } 3053