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