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 (node->hasTagName(HTMLNames::canvasTag)) 1165 mPictureSetDisabled = true; 1166 if (checkForPluginViewThatWantsFocus(nodeRenderer)) { 1167 bounds = absBounds; 1168 isUnclipped = true; 1169 takesFocus = true; 1170 type = PLUGIN_CACHEDNODETYPE; 1171 goto keepNode; 1172 } 1173 // Only use the root contentEditable element 1174 if (node->rendererIsEditable() && !node->parentOrHostNode()->rendererIsEditable()) { 1175 bounds = absBounds; 1176 takesFocus = true; 1177 type = CONTENT_EDITABLE_CACHEDNODETYPE; 1178 goto keepNode; 1179 } 1180 if (nodeRenderer->isRenderBlock()) { 1181 RenderBlock* renderBlock = (RenderBlock*) nodeRenderer; 1182 if (renderBlock->hasColumns()) { 1183 columnInfo = renderBlock->columnInfo(); 1184 columnGap = renderBlock->columnGap(); 1185 direction = renderBlock->style()->direction(); 1186 } 1187 } 1188 if ((hasClip != false || columnInfo != NULL) && lastChild) { 1189 clipTracker.grow(clipTracker.size() + 1); 1190 ClipColumnTracker& clip = clipTracker.last(); 1191 clip.mBounds = absBounds; 1192 clip.mLastChild = OneAfter(lastChild); 1193 clip.mNode = node; 1194 clip.mColumnInfo = columnInfo; 1195 clip.mColumnGap = columnGap; 1196 clip.mHasClip = hasClip; 1197 clip.mDirection = direction; 1198 if (columnInfo != NULL) { 1199 const IntRect& oRect = ((RenderBox*)nodeRenderer)->visualOverflowRect(); 1200 clip.mBounds.move(oRect.x(), oRect.y()); 1201 } 1202 } 1203 if (node->isTextNode() && mAllowableTypes != NORMAL_CACHEDNODE_BITS) { 1204 if (last->mSomeParentTakesFocus) // don't look at text inside focusable node 1205 continue; 1206 CachedNodeType checkType; 1207 if (isFocusableText(&walk, more, node, &checkType, 1208 &exported) == false) 1209 continue; 1210 #if DUMP_NAV_CACHE 1211 { 1212 char buffer[DEBUG_BUFFER_SIZE]; 1213 mDebug.init(buffer, sizeof(buffer)); 1214 mDebug.print("text link found: "); 1215 mDebug.wideString(exported); 1216 DUMP_NAV_LOGD("%s\n", buffer); 1217 } 1218 #endif 1219 type = checkType; 1220 // !!! test ! is the following line correctly needed for frames to work? 1221 cachedNode.init(node); 1222 const ClipColumnTracker& clipTrack = clipTracker.last(); 1223 const IntRect& clip = clipTrack.mHasClip ? clipTrack.mBounds : 1224 IntRect(0, 0, INT_MAX, INT_MAX); 1225 if (ConstructTextRects((WebCore::Text*) node, walk.mStart, 1226 (WebCore::Text*) walk.mFinalNode, walk.mEnd, globalOffsetX, 1227 globalOffsetY, &bounds, clip, &cachedNode.mCursorRing) == false) 1228 continue; 1229 absBounds = bounds; 1230 cachedNode.setBounds(bounds); 1231 if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) 1232 continue; 1233 if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) 1234 continue; 1235 computeCursorRings = true; 1236 isUnclipped = true; // FIXME: to hide or partially occlude synthesized links, each 1237 // focus ring will also need the offset and length of characters 1238 // used to produce it 1239 goto keepTextNode; 1240 } 1241 if (node->hasTagName(WebCore::HTMLNames::inputTag)) { 1242 HTMLInputElement* input = static_cast<HTMLInputElement*>(node); 1243 if (input->isTextField()) { 1244 if (input->readOnly()) 1245 continue; 1246 type = TEXT_INPUT_CACHEDNODETYPE; 1247 cachedInput.init(); 1248 cachedInput.setAutoComplete(input->autoComplete()); 1249 cachedInput.setSpellcheck(input->spellcheck()); 1250 cachedInput.setFormPointer(input->form()); 1251 cachedInput.setIsTextField(true); 1252 exported = input->value().threadsafeCopy(); 1253 cachedInput.setMaxLength(input->maxLength()); 1254 cachedInput.setTypeFromElement(input); 1255 // If this does not need to be threadsafe, we can use crossThreadString(). 1256 // See http://trac.webkit.org/changeset/49160. 1257 cachedInput.setName(input->name().string().threadsafeCopy()); 1258 // can't detect if this is drawn on top (example: deviant.com login parts) 1259 isUnclipped = isTransparent; 1260 } else if (input->isInputTypeHidden()) 1261 continue; 1262 else if (input->isRadioButton() || input->isCheckbox()) 1263 isTransparent = false; 1264 } else if (node->hasTagName(HTMLNames::textareaTag)) { 1265 HTMLTextAreaElement* area = static_cast<HTMLTextAreaElement*>(node); 1266 if (area->readOnly()) 1267 continue; 1268 cachedInput.init(); 1269 type = TEXT_INPUT_CACHEDNODETYPE; 1270 cachedInput.setFormPointer(area->form()); 1271 cachedInput.setIsTextArea(true); 1272 exported = area->value().threadsafeCopy(); 1273 } else if (node->hasTagName(HTMLNames::aTag)) { 1274 const HTMLAnchorElement* anchorNode = 1275 (const HTMLAnchorElement*) node; 1276 if (!anchorNode->isFocusable() && !HasTriggerEvent(node)) 1277 continue; 1278 if (node->disabled()) 1279 continue; 1280 hasMouseOver = NodeHasEventListeners(node, &eventNames().mouseoverEvent, 1); 1281 type = ANCHOR_CACHEDNODETYPE; 1282 KURL href = anchorNode->href(); 1283 if (!href.isEmpty() && !WebCore::protocolIsJavaScript(href.string())) 1284 // Set the exported string for all non-javascript anchors. 1285 exported = href.string().threadsafeCopy(); 1286 } else if (node->hasTagName(HTMLNames::selectTag)) { 1287 type = SELECT_CACHEDNODETYPE; 1288 } 1289 if (type == TEXT_INPUT_CACHEDNODETYPE) { 1290 RenderTextControl* renderText = 1291 static_cast<RenderTextControl*>(nodeRenderer); 1292 if (isFocus) 1293 cachedRoot->setSelection(renderText->selectionStart(), renderText->selectionEnd()); 1294 // FIXME: Are we sure there will always be a style and font, and it's correct? 1295 RenderStyle* style = nodeRenderer->style(); 1296 if (style) { 1297 isUnclipped |= !style->hasAppearance(); 1298 int lineHeight = -1; 1299 Length lineHeightLength = style->lineHeight(); 1300 // If the lineHeight is negative, WebTextView will calculate it 1301 // based on the text size, using the Paint. 1302 // See RenderStyle.computedLineHeight. 1303 if (lineHeightLength.isPositive()) 1304 lineHeight = style->computedLineHeight(); 1305 cachedInput.setLineHeight(lineHeight); 1306 cachedInput.setTextSize(style->font().size()); 1307 cachedInput.setIsRtlText(style->direction() == RTL 1308 || style->textAlign() == WebCore::RIGHT 1309 || style->textAlign() == WebCore::WEBKIT_RIGHT); 1310 } 1311 cachedInput.setPaddingLeft(renderText->paddingLeft() + renderText->borderLeft()); 1312 cachedInput.setPaddingTop(renderText->paddingTop() + renderText->borderTop()); 1313 cachedInput.setPaddingRight(renderText->paddingRight() + renderText->borderRight()); 1314 cachedInput.setPaddingBottom(renderText->paddingBottom() + renderText->borderBottom()); 1315 } 1316 takesFocus = true; 1317 bounds = absBounds; 1318 if (type != ANCHOR_CACHEDNODETYPE) { 1319 bool isFocusable = node->isKeyboardFocusable(NULL) || 1320 node->isMouseFocusable() || node->isFocusable(); 1321 if (isFocusable == false) { 1322 if (node->disabled()) 1323 continue; 1324 bool overOrOut = HasOverOrOut(node); 1325 bool hasTrigger = HasTriggerEvent(node); 1326 if (overOrOut == false && hasTrigger == false) 1327 continue; 1328 takesFocus = hasTrigger; 1329 } 1330 } 1331 computeCursorRings = true; 1332 keepNode: 1333 cachedNode.init(node); 1334 if (computeCursorRings == false) { 1335 cachedNode.setBounds(bounds); 1336 cachedNode.mCursorRing.append(bounds); 1337 } else if (ConstructPartRects(node, bounds, &cachedNode.mBounds, 1338 globalOffsetX, globalOffsetY, &cachedNode.mCursorRing, 1339 &imageCount) == false) 1340 continue; 1341 keepTextNode: 1342 if (nodeRenderer) { // area tags' node->renderer() == 0 1343 RenderStyle* style = nodeRenderer->style(); 1344 const void* styleDataPtr = style->ringData(); 1345 // to save time, see if we're pointing to the same style data as before 1346 if (lastStyleDataPtr != styleDataPtr) { 1347 lastStyleDataPtr = styleDataPtr; 1348 cachedColor.setFillColor(style->ringFillColor()); 1349 cachedColor.setInnerWidth(style->ringInnerWidth()); 1350 cachedColor.setOuterWidth(style->ringOuterWidth()); 1351 cachedColor.setOutset(style->ringOutset()); 1352 cachedColor.setPressedInnerColor(style->ringPressedInnerColor()); 1353 cachedColor.setPressedOuterColor(style->ringPressedOuterColor()); 1354 cachedColor.setRadius(style->ringRadius()); 1355 cachedColor.setSelectedInnerColor(style->ringSelectedInnerColor()); 1356 cachedColor.setSelectedOuterColor(style->ringSelectedOuterColor()); 1357 int oldSize = colorTracker.size(); 1358 colorIndex = FindColorIndex(colorTracker, cachedColor); 1359 if (colorIndex == oldSize) 1360 cachedFrame->add(cachedColor); 1361 } 1362 } else 1363 colorIndex = 0; 1364 IntRect clip = hasClip ? bounds : absBounds; 1365 size_t clipIndex = clipTracker.size(); 1366 if (clipTracker.last().mNode == node) 1367 clipIndex -= 1; 1368 while (--clipIndex > 0) { 1369 const ClipColumnTracker& clipTrack = clipTracker.at(clipIndex); 1370 if (clipTrack.mHasClip == false) { 1371 adjustForColumns(clipTrack, &cachedNode, &absBounds, static_cast<RenderBlock*>(nodeRenderer)); 1372 continue; 1373 } 1374 const IntRect& parentClip = clipTrack.mBounds; 1375 if (hasClip == false && type == ANCHOR_CACHEDNODETYPE) 1376 clip = parentClip; 1377 else 1378 clip.intersect(parentClip); 1379 hasClip = true; 1380 } 1381 bool isInLayer = false; 1382 #if USE(ACCELERATED_COMPOSITING) 1383 // If this renderer has a composited parent layer (including itself), 1384 // add the node to the cached layer. 1385 // FIXME: does not work for area rects 1386 RenderLayer* enclosingLayer = nodeRenderer->enclosingLayer(); 1387 if (enclosingLayer && enclosingLayer->enclosingCompositingLayer()) { 1388 LayerAndroid* layer = layerTracker.last().mLayer; 1389 if (layer) { 1390 const IntRect& layerClip = layerTracker.last().mBounds; 1391 if (!layerClip.isEmpty() && !cachedNode.clip(layerClip)) { 1392 DBG_NAV_LOGD("skipped on layer clip %d", cacheIndex); 1393 continue; // skip this node if outside of the clip 1394 } 1395 isInLayer = true; 1396 isUnclipped = true; // assume that layers do not have occluded nodes 1397 hasClip = false; 1398 AddLayer(cachedFrame, cachedFrame->size(), layerClip.location(), 1399 layer->uniqueId()); 1400 } 1401 } 1402 #endif 1403 if (hasClip) { 1404 if (clip.isEmpty()) 1405 continue; // skip this node if clip prevents all drawing 1406 else if (cachedNode.clip(clip) == false) 1407 continue; // skip this node if outside of the clip 1408 } 1409 cachedNode.setNavableRects(); 1410 cachedNode.setColorIndex(colorIndex); 1411 cachedNode.setExport(exported); 1412 cachedNode.setHasCursorRing(hasCursorRing); 1413 cachedNode.setHasMouseOver(hasMouseOver); 1414 cachedNode.setHitBounds(absBounds); 1415 cachedNode.setIndex(cacheIndex); 1416 cachedNode.setIsFocus(isFocus); 1417 cachedNode.setIsInLayer(isInLayer); 1418 cachedNode.setIsTransparent(isTransparent); 1419 cachedNode.setIsUnclipped(isUnclipped); 1420 cachedNode.setOriginalAbsoluteBounds(originalAbsBounds); 1421 cachedNode.setParentIndex(last->mCachedNodeIndex); 1422 cachedNode.setParentGroup(ParentWithChildren(node)); 1423 cachedNode.setSingleImage(imageCount == 1); 1424 cachedNode.setTabIndex(tabIndex); 1425 cachedNode.setType(type); 1426 if (type == TEXT_INPUT_CACHEDNODETYPE) { 1427 cachedFrame->add(cachedInput); 1428 cachedNode.setDataIndex(textInputIndex); 1429 textInputIndex++; 1430 } else 1431 cachedNode.setDataIndex(-1); 1432 #if DUMP_NAV_CACHE 1433 cachedNode.mDebug.mNodeIndex = nodeIndex; 1434 cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex( 1435 node, nodeIndex, (Node*) cachedNode.parentGroup()); 1436 #endif 1437 cachedFrame->add(cachedNode); 1438 { 1439 int lastIndex = cachedFrame->size() - 1; 1440 if (node == focused) { 1441 CachedNode* cachedNodePtr = cachedFrame->getIndex(lastIndex); 1442 cachedRoot->setCachedFocus(cachedFrame, cachedNodePtr); 1443 } 1444 if (lastChild != NULL) { 1445 tracker.grow(tracker.size() + 1); 1446 FocusTracker& working = tracker.last(); 1447 working.mCachedNodeIndex = lastIndex; 1448 working.mLastChild = OneAfter(lastChild); 1449 last = &tracker.at(tracker.size() - 2); 1450 working.mSomeParentTakesFocus = last->mSomeParentTakesFocus | takesFocus; 1451 } 1452 } 1453 cacheIndex++; 1454 } 1455 while (tracker.size() > 1) { 1456 FocusTracker* last = &tracker.last(); 1457 int lastChildIndex = cachedFrame->size() - 1; 1458 if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex)) 1459 cacheIndex--; 1460 tracker.removeLast(); 1461 } 1462 } 1463 1464 bool CacheBuilder::CleanUpContainedNodes(CachedRoot* cachedRoot, 1465 CachedFrame* cachedFrame, const FocusTracker* last, int lastChildIndex) 1466 { 1467 // if outer is body, disable outer 1468 // or if there's more than one inner, disable outer 1469 // or if inner is keyboard focusable, disable outer 1470 // else disable inner by removing it 1471 int childCount = lastChildIndex - last->mCachedNodeIndex; 1472 if (childCount == 0) 1473 return false; 1474 CachedNode* lastCached = cachedFrame->getIndex(last->mCachedNodeIndex); 1475 Node* lastNode = (Node*) lastCached->nodePointer(); 1476 if ((childCount > 1 && lastNode->hasTagName(HTMLNames::selectTag) == false) || 1477 lastNode->hasTagName(HTMLNames::bodyTag) || 1478 lastNode->hasTagName(HTMLNames::formTag)) { 1479 lastCached->setBounds(IntRect(0, 0, 0, 0)); 1480 lastCached->mCursorRing.clear(); 1481 lastCached->setNavableRects(); 1482 return false; 1483 } 1484 CachedNode* onlyChildCached = cachedFrame->lastNode(); 1485 Node* onlyChild = (Node*) onlyChildCached->nodePointer(); 1486 bool outerIsMouseMoveOnly = 1487 lastNode->isKeyboardFocusable(NULL) == false && 1488 lastNode->isMouseFocusable() == false && 1489 lastNode->isFocusable() == false && 1490 HasOverOrOut(lastNode) == true && 1491 HasTriggerEvent(lastNode) == false; 1492 if (onlyChildCached->parent() == lastCached) 1493 onlyChildCached->setParentIndex(lastCached->parentIndex()); 1494 bool hasFocus = lastCached->isFocus() || onlyChildCached->isFocus(); 1495 if (outerIsMouseMoveOnly || onlyChild->isKeyboardFocusable(NULL) 1496 || onlyChildCached->isPlugin()) { 1497 int index = lastCached->index(); 1498 *lastCached = *onlyChildCached; 1499 lastCached->setIndex(index); 1500 CachedFrame* frame = cachedFrame->hasFrame(lastCached); 1501 if (frame) 1502 frame->setIndexInParent(index); 1503 } 1504 cachedFrame->removeLast(); 1505 if (hasFocus) 1506 cachedRoot->setCachedFocus(cachedFrame, cachedFrame->lastNode()); 1507 return true; 1508 } 1509 1510 Node* CacheBuilder::currentFocus() const 1511 { 1512 Frame* frame = FrameAnd(this); 1513 Document* doc = frame->document(); 1514 if (doc != NULL) { 1515 Node* focus = doc->focusedNode(); 1516 if (focus != NULL) 1517 return focus; 1518 } 1519 Frame* child = frame->tree()->firstChild(); 1520 while (child) { 1521 CacheBuilder* cacheBuilder = Builder(child); 1522 Node* focus = cacheBuilder->currentFocus(); 1523 if (focus) 1524 return focus; 1525 child = child->tree()->nextSibling(); 1526 } 1527 return NULL; 1528 } 1529 1530 static bool strCharCmp(const char* matches, const UChar* test, int wordLength, 1531 int wordCount) 1532 { 1533 for (int index = 0; index < wordCount; index++) { 1534 for (int inner = 0; inner < wordLength; inner++) { 1535 if (matches[inner] != test[inner]) { 1536 matches += wordLength; 1537 goto next; 1538 } 1539 } 1540 return true; 1541 next: 1542 ; 1543 } 1544 return false; 1545 } 1546 1547 static const int stateTwoLetter[] = { 1548 0x02060c00, // A followed by: [KLRSZ] 1549 0x00000000, // B 1550 0x00084001, // C followed by: [AOT] 1551 0x00000014, // D followed by: [CE] 1552 0x00000000, // E 1553 0x00001800, // F followed by: [LM] 1554 0x00100001, // G followed by: [AU] 1555 0x00000100, // H followed by: [I] 1556 0x00002809, // I followed by: [ADLN] 1557 0x00000000, // J 1558 0x01040000, // K followed by: [SY] 1559 0x00000001, // L followed by: [A] 1560 0x000ce199, // M followed by: [ADEHINOPST] 1561 0x0120129c, // N followed by: [CDEHJMVY] 1562 0x00020480, // O followed by: [HKR] 1563 0x00420001, // P followed by: [ARW] 1564 0x00000000, // Q 1565 0x00000100, // R followed by: [I] 1566 0x0000000c, // S followed by: [CD] 1567 0x00802000, // T followed by: [NX] 1568 0x00080000, // U followed by: [T] 1569 0x00080101, // V followed by: [AIT] 1570 0x01200101 // W followed by: [AIVY] 1571 }; 1572 1573 static const char firstIndex[] = { 1574 0, 5, 5, 8, 10, 10, 12, 14, 1575 15, 19, 19, 21, 22, 32, 40, 43, 1576 46, 46, 47, 49, 51, 52, 55, 59 1577 }; 1578 1579 // from http://infolab.stanford.edu/~manku/bitcount/bitcount.html 1580 #define TWO(c) (0x1u << (c)) 1581 #define MASK(c) (((unsigned int)(-1)) / (TWO(TWO(c)) + 1u)) 1582 #define COUNT(x,c) ((x) & MASK(c)) + (((x) >> (TWO(c))) & MASK(c)) 1583 1584 int bitcount (unsigned int n) 1585 { 1586 n = COUNT(n, 0); 1587 n = COUNT(n, 1); 1588 n = COUNT(n, 2); 1589 n = COUNT(n, 3); 1590 return COUNT(n, 4); 1591 } 1592 1593 #undef TWO 1594 #undef MASK 1595 #undef COUNT 1596 1597 static bool isUnicodeSpace(UChar ch) 1598 { 1599 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0; 1600 } 1601 1602 static bool validZip(int stateIndex, const UChar* zipPtr) 1603 { 1604 static const struct { 1605 char mLow; 1606 char mHigh; 1607 char mException1; 1608 char mException2; 1609 } zipRange[] = { 1610 { 99, 99, -1, -1 }, // AK Alaska 1611 { 35, 36, -1, -1 }, // AL Alabama 1612 { 71, 72, -1, -1 }, // AR Arkansas 1613 { 96, 96, -1, -1 }, // AS American Samoa 1614 { 85, 86, -1, -1 }, // AZ Arizona 1615 { 90, 96, -1, -1 }, // CA California 1616 { 80, 81, -1, -1 }, // CO Colorado 1617 { 6, 6, -1, -1 }, // CT Connecticut 1618 { 20, 20, -1, -1 }, // DC District of Columbia 1619 { 19, 19, -1, -1 }, // DE Delaware 1620 { 32, 34, -1, -1 }, // FL Florida 1621 { 96, 96, -1, -1 }, // FM Federated States of Micronesia 1622 { 30, 31, -1, -1 }, // GA Georgia 1623 { 96, 96, -1, -1 }, // GU Guam 1624 { 96, 96, -1, -1 }, // HI Hawaii 1625 { 50, 52, -1, -1 }, // IA Iowa 1626 { 83, 83, -1, -1 }, // ID Idaho 1627 { 60, 62, -1, -1 }, // IL Illinois 1628 { 46, 47, -1, -1 }, // IN Indiana 1629 { 66, 67, 73, -1 }, // KS Kansas 1630 { 40, 42, -1, -1 }, // KY Kentucky 1631 { 70, 71, -1, -1 }, // LA Louisiana 1632 { 1, 2, -1, -1 }, // MA Massachusetts 1633 { 20, 21, -1, -1 }, // MD Maryland 1634 { 3, 4, -1, -1 }, // ME Maine 1635 { 96, 96, -1, -1 }, // MH Marshall Islands 1636 { 48, 49, -1, -1 }, // MI Michigan 1637 { 55, 56, -1, -1 }, // MN Minnesota 1638 { 63, 65, -1, -1 }, // MO Missouri 1639 { 96, 96, -1, -1 }, // MP Northern Mariana Islands 1640 { 38, 39, -1, -1 }, // MS Mississippi 1641 { 55, 56, -1, -1 }, // MT Montana 1642 { 27, 28, -1, -1 }, // NC North Carolina 1643 { 58, 58, -1, -1 }, // ND North Dakota 1644 { 68, 69, -1, -1 }, // NE Nebraska 1645 { 3, 4, -1, -1 }, // NH New Hampshire 1646 { 7, 8, -1, -1 }, // NJ New Jersey 1647 { 87, 88, 86, -1 }, // NM New Mexico 1648 { 88, 89, 96, -1 }, // NV Nevada 1649 { 10, 14, 0, 6 }, // NY New York 1650 { 43, 45, -1, -1 }, // OH Ohio 1651 { 73, 74, -1, -1 }, // OK Oklahoma 1652 { 97, 97, -1, -1 }, // OR Oregon 1653 { 15, 19, -1, -1 }, // PA Pennsylvania 1654 { 6, 6, 0, 9 }, // PR Puerto Rico 1655 { 96, 96, -1, -1 }, // PW Palau 1656 { 2, 2, -1, -1 }, // RI Rhode Island 1657 { 29, 29, -1, -1 }, // SC South Carolina 1658 { 57, 57, -1, -1 }, // SD South Dakota 1659 { 37, 38, -1, -1 }, // TN Tennessee 1660 { 75, 79, 87, 88 }, // TX Texas 1661 { 84, 84, -1, -1 }, // UT Utah 1662 { 22, 24, 20, -1 }, // VA Virginia 1663 { 6, 9, -1, -1 }, // VI Virgin Islands 1664 { 5, 5, -1, -1 }, // VT Vermont 1665 { 98, 99, -1, -1 }, // WA Washington 1666 { 53, 54, -1, -1 }, // WI Wisconsin 1667 { 24, 26, -1, -1 }, // WV West Virginia 1668 { 82, 83, -1, -1 } // WY Wyoming 1669 }; 1670 1671 int zip = zipPtr[0] - '0'; 1672 zip *= 10; 1673 zip += zipPtr[1] - '0'; 1674 int low = zipRange[stateIndex].mLow; 1675 int high = zipRange[stateIndex].mHigh; 1676 if (zip >= low && zip <= high) 1677 return true; 1678 if (zip == zipRange[stateIndex].mException1) 1679 return true; 1680 if (zip == zipRange[stateIndex].mException2) 1681 return true; 1682 return false; 1683 } 1684 1685 #define MAX_PLACE_NAME_LENGTH 25 // the longest allowable one word place name 1686 1687 CacheBuilder::FoundState CacheBuilder::FindAddress(const UChar* chars, 1688 unsigned length, int* start, int* end, bool caseInsensitive) 1689 { 1690 FindState addressState; 1691 FindReset(&addressState); 1692 addressState.mWords[0] = addressState.mStarts[0] = chars; 1693 addressState.mCaseInsensitive = caseInsensitive; 1694 FoundState state = FindPartialAddress(chars, chars, length, &addressState); 1695 if (state == FOUND_PARTIAL && addressState.mProgress == ZIP_CODE && 1696 addressState.mNumberCount == 0) { 1697 addressState.mProgress = FIND_STREET; 1698 state = FindPartialAddress(NULL, NULL, 0, &addressState); 1699 } 1700 *start = addressState.mStartResult; 1701 *end = addressState.mEndResult; 1702 return state; 1703 } 1704 1705 CacheBuilder::FoundState CacheBuilder::FindPartialAddress(const UChar* baseChars, 1706 const UChar* chars, unsigned length, FindState* s) 1707 { 1708 // lower case letters are optional; trailing asterisk is optional 's' 1709 static char const* const longStreetNames[] = { 1710 "\x04" "LleY" "\x04" "NneX" "\x05" "RCade" "\x05" "VEnue" "\x06" "LAMEDA", // A 1711 "\x04" "aYoU" "\x04" "eaCH" "\x03" "eND" "\x05" "LuFf*" "\x05" "oTtoM" 1712 "\x08" "ouLeVarD" "\x05" "Ranch" "\x05" "RidGe" "\x05" "RooK*" 1713 "\x04" "urG*" "\x05" "YPass" "\x07" "roadWAY", // B 1714 "\x05" "AMINO" 1715 "\x03" "amP" "\x05" "anYoN" "\x03" "aPE" "\x07" "auSeWaY" "\x06" "enTeR*" 1716 "\x06" "IRcle*" "\x05" "LiFf*" "\x03" "LuB" "\x05" "oMmoN" "\x06" "ORner*" 1717 "\x05" "ouRSE" "\x05" "ourT*" "\x04" "oVe*" "\x04" "ReeK" "\x07" "REScent" 1718 "\x04" "ReST" "\x07" "ROSSING" "\x08" "ROSSROAD" "\x04" "URVe" 1719 "\x05" "AMINO" "\x06" "IRCULO" "\x07" "REScent", // C 1720 "\x03" "aLe" "\x02" "aM" "\x05" "iVide" "\x05" "Rive*", // D 1721 "\x06" "STate*" "\x09" "XPresswaY" "\x09" "XTension*", // E 1722 "\x04" "ALL*" "\x04" "eRrY" "\x05" "ieLD*" "\x04" "LaT*" "\x04" "oRD*" 1723 "\x05" "oReST" "\x05" "oRGe*" "\x04" "oRK*" "\x03" "orT" "\x06" "reeWaY", // F 1724 "\x06" "arDeN*" "\x06" "aTeWaY" "\x04" "LeN*" "\x05" "ReeN*" "\x05" "RoVe*", // G 1725 "\x06" "arBoR*" "\x04" "aVeN" "\x06" "eighTS" "\x06" "ighWaY" "\x04" "iLl*" 1726 "\x05" "OLloW", // H 1727 "\x04" "NLeT" "\x06" "Sland*" "\x03" "SLE", // I 1728 "\x08" "unCTion*", // J 1729 "\x03" "eY*" "\x05" "NoLl*", // K 1730 "\x04" "aKe*" "\x03" "AND" "\x06" "aNDinG" "\x03" "aNe" "\x05" "iGhT*" 1731 "\x03" "oaF" "\x04" "oCK*" "\x04" "oDGe" "\x03" "OOP", // L 1732 "\x03" "ALL" "\x05" "aNoR*" "\x06" "eaDoW*" "\x03" "EWS" "\x04" "iLl*" 1733 "\x06" "iSsioN" "\x07" "oTorWaY" "\x04" "ounT" "\x08" "ounTaiN*", // M 1734 "\x03" "eCK", // N 1735 "\x06" "RCHard" "\x03" "VAL" "\x07" "verPASs", // O 1736 "\x04" "ARK*" "\x07" "arKWaY*" "\x03" "ASS" "\x06" "aSsaGE" "\x03" "ATH" 1737 "\x03" "IKE" "\x04" "iNE*" "\x04" "Lace" "\x05" "LaiN*" "\x04" "LaZa" 1738 "\x05" "oinT*" "\x04" "oRT*" "\x06" "Rairie" "\x06" "RIVADA", // P 1739 NULL, 1740 "\x05" "ADiaL" "\x03" "AMP" "\x04" "aNCH" "\x05" "aPiD*" 1741 "\x03" "eST" 1742 "\x05" "iDGe*" "\x04" "IVer" "\x04" "oaD*" "\x04" "ouTE" "\x02" "OW" 1743 "\x02" "UE" "\x02" "UN", // R 1744 "\x05" "HoaL*" "\x05" "HoRe*" "\x05" "KyWaY" "\x06" "PrinG*" "\x04" "PUR*" 1745 "\x06" "Quare*" "\x06" "TAtion" "\x08" "TRAvenue" "\x05" "TReaM" 1746 "\x06" "Treet*" "\x05" "uMmiT" "\x07" "PeeDWaY", // S 1747 "\x06" "ERrace" "\x09" "hRoughWaY" "\x04" "RaCE" "\x04" "RAcK" "\x09" "RaFficwaY" 1748 "\x04" "RaiL" "\x05" "UNneL" "\x07" "urnPiKE", // T 1749 "\x08" "nderPASs" "\x05" "Nion*", // U 1750 "\x06" "aLleY*" "\x06" "IAduct" "\x04" "ieW*" "\x07" "iLlaGe*" "\x04" "iLle" 1751 "\x04" "ISta", // V 1752 "\x04" "ALK*" "\x03" "ALL" "\x03" "AY*" "\x04" "eLl*", // W 1753 "\x03" "ING" "\x02" "RD", // X 1754 }; 1755 1756 static char const* const longStateNames[] = { 1757 "\x8e" "la" "\x85" "bama" "\x02" "\x84" "ska" "\x01" "\x8f" "merican Samoa" "\x04" 1758 "\x91" "r" "\x86" "izona" "\x05" "\x87" "kansas" "\x03", 1759 NULL, 1760 "\x8b" "alifornia" "\x06" "\x95" "o" "\x87" "lorado" "\x07" "\x8a" "nnecticut" "\x08", 1761 "\x89" "elaware" "\x0a" "\x95" "istrict of Columbia" "\x09", 1762 NULL, 1763 "\x9f" "ederated States of Micronesia" "\x0c" "\x88" "lorida" "\x0b", 1764 "\x85" "uam" "\x0e" "\x88" "eorgia" "\x0d", 1765 "\x87" "awaii" "\x0f", 1766 "\x86" "daho" "\x11" "\x89" "llinois" "\x12" "\x88" "ndiana" "\x13" "\x85" 1767 "owa" "\x10", 1768 NULL, 1769 "\x87" "ansas" "\x14" "\x89" "entucky" "\x15", 1770 "\x8a" "ouisiana" "\x16", 1771 "\x86" "aine" "\x19" "\x99" "ar" "\x8e" "shall Islands" "\x1a" "\x86" "yland" "\x18" 1772 "\x8e" "assachusetts" "\x17" "\x93" "i" "\x87" "chigan" "\x1b" 1773 "\x88" "nnesota" "\x1c" "\x93" "iss" "\x88" "issippi" "\x1f" "\x85" 1774 "ouri" "\x1d" "\x88" "ontana" "\x20", 1775 "\x90" "e" "\x87" "braska" "\x23" "\x85" "vada" "\x27" "\xa5" "ew " "\x8a" 1776 "Hampshire" "\x24" "\x87" "Jersey" "\x25" "\x87" "Mexico" "\x26" 1777 "\x85" "York" "\x28" "\x98" "orth " "\x89" "Carolina" "\x21" "\x87" 1778 "Dakota" "\x22" "\x99" "orthern Mariana Islands" "\x1e", 1779 "\x85" "hio" "\x29" "\x89" "klahoma" "\x2a" "\x87" "regon" "\x2b", 1780 "\x86" "alau" "\x2e" "\x8d" "ennsylvania" "\x2c" "\x8c" "uerto Rico" "\x2d", 1781 NULL, 1782 "\x8d" "hode Island" "\x2f", 1783 "\x98" "outh " "\x89" "Carolina" "\x30" "\x87" "Dakota" "\x31", 1784 "\x90" "e" "\x88" "nnessee" "\x32" "\x84" "xas" "\x33", 1785 "\x85" "tah" "\x34", 1786 "\x88" "ermont" "\x37" "\x94" "irgin" "\x89" " Islands" "\x36" "\x83" "ia" "\x35", 1787 "\x8b" "ashington" "\x38" "\x8e" "est Virginia" "\x3a" "\x8a" "isconsin" "\x39" 1788 "\x88" "yoming" "\x3b" 1789 }; 1790 1791 #if 0 // DEBUG_NAV_UI 1792 static char const* const progressNames[] = { 1793 "NO_ADDRESS", 1794 "SKIP_TO_SPACE", 1795 "HOUSE_NUMBER", 1796 "NUMBER_TRAILING_SPACE", 1797 "ADDRESS_LINE", 1798 "STATE_NAME", 1799 "SECOND_HALF", 1800 "ZIP_CODE", 1801 "PLUS_4", 1802 "FIND_STREET" 1803 }; 1804 #endif 1805 // strategy: US only support at first 1806 // look for a 1 - 5 digit number for a street number (no support for 'One Microsoft Way') 1807 // ignore if preceded by '#', Suite, Ste, Rm 1808 // look for two or more words (up to 5? North Frank Lloyd Wright Blvd) 1809 // note: "The Circle at North Hills St." has six words, and a lower 'at' -- allow at, by, of, in, the, and, ... ? 1810 // if a word starts with a lowercase letter, no match 1811 // allow: , . - # / (for 1/2) ' " 1812 // don't look for street name type yet 1813 // look for one or two delimiters to represent possible 2nd addr line and city name 1814 // look for either full state name, or state two letters, and/or zip code (5 or 9 digits) 1815 // now look for street suffix, either in full or abbreviated form, with optional 's' if there's an asterisk 1816 1817 s->mCurrentStart = chars; 1818 s->mEnd = chars + length; 1819 int candIndex = 0; 1820 bool retryState; 1821 bool mustBeAllUpper = false; 1822 bool secondHalf = false; 1823 chars -= 1; 1824 UChar ch = s->mCurrent; 1825 while (++chars <= s->mEnd) { 1826 UChar prior = ch; 1827 ch = chars < s->mEnd ? *chars : ' '; 1828 switch (s->mProgress) { 1829 case NO_ADDRESS: 1830 if (WTF::isASCIIDigit(ch) == false) { 1831 if (ch != 'O') // letter 'O', not zero 1832 continue; 1833 if (s->mEnd - chars < 3) 1834 continue; 1835 prior = *++chars; 1836 ch = *++chars; 1837 if ((prior != 'n' || ch != 'e') && (prior != 'N' || ch != 'E')) 1838 continue; 1839 if (isUnicodeSpace(*++chars) == false) 1840 continue; 1841 s->mProgress = ADDRESS_LINE; 1842 s->mStartResult = chars - 3 - s->mCurrentStart; 1843 continue; 1844 } 1845 if (isUnicodeSpace(prior) == false) { 1846 s->mProgress = SKIP_TO_SPACE; 1847 continue; 1848 } 1849 s->mNumberCount = 1; 1850 s->mProgress = HOUSE_NUMBER; 1851 s->mStartResult = chars - s->mCurrentStart; 1852 continue; 1853 case SKIP_TO_SPACE: 1854 if (isUnicodeSpace(ch) == false) 1855 continue; 1856 break; 1857 case HOUSE_NUMBER: 1858 if (WTF::isASCIIDigit(ch)) { 1859 if (++s->mNumberCount >= 6) 1860 s->mProgress = SKIP_TO_SPACE; 1861 continue; 1862 } 1863 if (WTF::isASCIIUpper(ch)) { // allow one letter after house number, e.g. 12A SKOLFIELD PL, HARPSWELL, ME 04079 1864 if (WTF::isASCIIDigit(prior) == false) 1865 s->mProgress = SKIP_TO_SPACE; 1866 continue; 1867 } 1868 if (ch == '-') { 1869 if (s->mNumberCount > 0) { // permit 21-23 ELM ST 1870 ++s->mNumberCount; 1871 continue; 1872 } 1873 } 1874 s->mNumberCount = 0; 1875 s->mProgress = NUMBER_TRAILING_SPACE; 1876 case NUMBER_TRAILING_SPACE: 1877 if (isUnicodeSpace(ch)) 1878 continue; 1879 if (0 && WTF::isASCIIDigit(ch)) { 1880 s->mNumberCount = 1; 1881 s->mProgress = HOUSE_NUMBER; 1882 s->mStartResult = chars - s->mCurrentStart; 1883 continue; 1884 } 1885 if (WTF::isASCIIDigit(ch) == false && WTF::isASCIIUpper(ch) == false) 1886 break; 1887 s->mProgress = ADDRESS_LINE; 1888 case ADDRESS_LINE: 1889 if (WTF::isASCIIAlpha(ch) || ch == '\'' || ch == '-' || ch == '&' || ch == '(' || ch == ')') { 1890 if (++s->mLetterCount > 1) { 1891 s->mNumberWords &= ~(1 << s->mWordCount); 1892 continue; 1893 } 1894 if (s->mNumberCount >= 5) 1895 break; 1896 // FIXME: the test below was added to give up on a non-address, but it 1897 // incorrectly discards addresses where the house number is in one node 1898 // and the street name is in the next; I don't recall what the failing case 1899 // is that suggested this fix. 1900 // if (s->mWordCount == 0 && s->mContinuationNode) 1901 // return FOUND_NONE; 1902 s->newWord(baseChars, chars); 1903 if (WTF::isASCIILower(ch) && s->mNumberCount == 0) 1904 s->mFirstLower = chars; 1905 s->mNumberCount = 0; 1906 if (WTF::isASCIILower(ch) || (WTF::isASCIIAlpha(ch) == false && ch != '-')) 1907 s->mNumberWords &= ~(1 << s->mWordCount); 1908 s->mUnparsed = true; 1909 continue; 1910 } else if (s->mLetterCount >= MAX_PLACE_NAME_LENGTH) { 1911 break; 1912 } else if (s->mFirstLower != NULL) { 1913 if (s->mCaseInsensitive) 1914 goto resetWord; 1915 size_t length = chars - s->mFirstLower; 1916 if (length > 3) 1917 break; 1918 if (length == 3 && strCharCmp("and" "the", s->mFirstLower, 3, 2) == false) 1919 break; 1920 if (length == 2 && strCharCmp("at" "by" "el" "in" "of", s->mFirstLower, 2, 5) == false) 1921 break; 1922 goto resetWord; 1923 } 1924 if (ch == ',' || ch == '*') { // delimits lines 1925 // asterisk as delimiter: http://www.sa.sc.edu/wellness/members.html 1926 if (++s->mLineCount > 5) 1927 break; 1928 goto lookForState; 1929 } 1930 if (isUnicodeSpace(ch) || prior == '-') { 1931 lookForState: 1932 if (s->mUnparsed == false) 1933 continue; 1934 const UChar* candidate = s->mWords[s->mWordCount]; 1935 UChar firstLetter = candidate[0]; 1936 if (WTF::isASCIIUpper(firstLetter) == false && WTF::isASCIIDigit(firstLetter) == false) 1937 goto resetWord; 1938 s->mWordCount++; 1939 if ((s->mWordCount == 2 && s->mNumberWords == 3 && WTF::isASCIIDigit(s->mWords[1][1])) || // choose second of 8888 333 Main 1940 (s->mWordCount >= sizeof(s->mWords) / sizeof(s->mWords[0]) - 1)) { // subtract 1 since state names may have two parts 1941 // search for simple number already stored since first potential house # didn't pan out 1942 if (s->mNumberWords == 0) 1943 break; 1944 int shift = 0; 1945 while ((s->mNumberWords & (1 << shift)) == 0) 1946 shift++; 1947 s->mNumberWords >>= ++shift; 1948 if (s->mBases[0] != s->mBases[shift]) // if we're past the original node, bail 1949 break; 1950 s->shiftWords(shift); 1951 s->mStartResult = s->mWords[0] - s->mStarts[0]; 1952 s->mWordCount -= shift; 1953 // FIXME: need to adjust lineCount to account for discarded delimiters 1954 } 1955 if (s->mWordCount < 4) 1956 goto resetWord; 1957 firstLetter -= 'A'; 1958 if (firstLetter > 'W' - 'A') 1959 goto resetWord; 1960 UChar secondLetter = candidate[1]; 1961 if (prior == '-') 1962 s->mLetterCount--; // trim trailing dashes, to accept CA-94043 1963 if (s->mLetterCount == 2) { 1964 secondLetter -= 'A'; 1965 if (secondLetter > 'Z' - 'A') 1966 goto resetWord; 1967 if ((stateTwoLetter[firstLetter] & 1 << secondLetter) != 0) { 1968 // special case to ignore 'et al' 1969 if (strCharCmp("ET", s->mWords[s->mWordCount - 2], 2, 1) == false) { 1970 s->mStateWord = s->mWordCount - 1; 1971 s->mZipHint = firstIndex[firstLetter] + 1972 bitcount(stateTwoLetter[firstLetter] & ((1 << secondLetter) - 1)); 1973 goto foundStateName; 1974 } 1975 } 1976 goto resetWord; 1977 } 1978 s->mStates = longStateNames[firstLetter]; 1979 if (s->mStates == NULL) 1980 goto resetWord; 1981 mustBeAllUpper = false; 1982 s->mProgress = STATE_NAME; 1983 unsigned char section = s->mStates[0]; 1984 ASSERT(section > 0x80); 1985 s->mSectionLength = section & 0x7f; 1986 candIndex = 1; 1987 secondHalf = false; 1988 s->mStateWord = s->mWordCount - 1; 1989 goto stateName; 1990 } 1991 if (WTF::isASCIIDigit(ch)) { 1992 if (s->mLetterCount == 0) { 1993 if (++s->mNumberCount > 1) 1994 continue; 1995 if (s->mWordCount == 0 && s->mContinuationNode) 1996 return FOUND_NONE; 1997 s->newWord(baseChars, chars); 1998 s->mNumberWords |= 1 << s->mWordCount; 1999 s->mUnparsed = true; 2000 } 2001 continue; 2002 } 2003 if (ch == '.') { // optionally can follow letters 2004 if (s->mLetterCount == 0) 2005 break; 2006 if (s->mNumberCount > 0) 2007 break; 2008 continue; 2009 } 2010 if (ch == '/') // between numbers (1/2) between words (12 Main / Ste 4d) 2011 goto resetWord; 2012 if (ch == '#') // can precede numbers, allow it to appear randomly 2013 goto resetWord; 2014 if (ch == '"') // sometimes parts of addresses are quoted (FIXME: cite an example here) 2015 continue; 2016 break; 2017 case SECOND_HALF: 2018 if (WTF::isASCIIAlpha(ch)) { 2019 if (s->mLetterCount == 0) { 2020 s->newWord(baseChars, chars); 2021 s->mWordCount++; 2022 } 2023 s->mLetterCount++; 2024 continue; 2025 } 2026 if (WTF::isASCIIDigit(ch) == false) { 2027 if (s->mLetterCount > 0) { 2028 s->mProgress = STATE_NAME; 2029 candIndex = 0; 2030 secondHalf = true; 2031 goto stateName; 2032 } 2033 continue; 2034 } 2035 s->mProgress = ADDRESS_LINE; 2036 goto resetState; 2037 case STATE_NAME: 2038 stateName: 2039 // pick up length of first section 2040 do { 2041 int stateIndex = 1; 2042 int skip = 0; 2043 int prefix = 0; 2044 bool subStr = false; 2045 do { 2046 unsigned char match = s->mStates[stateIndex]; 2047 if (match >= 0x80) { 2048 if (stateIndex == s->mSectionLength) 2049 break; 2050 subStr = true; 2051 // if (skip > 0) 2052 // goto foundStateName; 2053 prefix = candIndex; 2054 skip = match & 0x7f; 2055 match = s->mStates[++stateIndex]; 2056 } 2057 UChar candChar = s->mWords[s->mWordCount - 1][candIndex]; 2058 if (mustBeAllUpper && WTF::isASCIILower(candChar)) 2059 goto skipToNext; 2060 if (match != candChar) { 2061 if (match != WTF::toASCIILower(candChar)) { 2062 skipToNext: 2063 if (subStr == false) 2064 break; 2065 if (stateIndex == s->mSectionLength) { 2066 if (secondHalf) { 2067 s->mProgress = ADDRESS_LINE; 2068 goto resetState; 2069 } 2070 break; 2071 } 2072 stateIndex += skip; 2073 skip = 0; 2074 candIndex = prefix; 2075 continue; // try next substring 2076 } 2077 mustBeAllUpper = true; 2078 } 2079 int nextindex = stateIndex + 1; 2080 if (++candIndex >= s->mLetterCount && s->mStates[nextindex] == ' ') { 2081 s->mProgress = SECOND_HALF; 2082 s->mStates += nextindex; 2083 s->mSectionLength -= nextindex; 2084 goto resetWord; 2085 } 2086 if (nextindex + 1 == s->mSectionLength || skip == 2) { 2087 s->mZipHint = s->mStates[nextindex] - 1; 2088 goto foundStateName; 2089 } 2090 stateIndex += 1; 2091 skip -= 1; 2092 } while (true); 2093 s->mStates += s->mSectionLength; 2094 ASSERT(s->mStates[0] == 0 || (unsigned) s->mStates[0] > 0x80); 2095 s->mSectionLength = s->mStates[0] & 0x7f; 2096 candIndex = 1; 2097 subStr = false; 2098 } while (s->mSectionLength != 0); 2099 s->mProgress = ADDRESS_LINE; 2100 goto resetState; 2101 foundStateName: 2102 s->mEndResult = chars - s->mCurrentStart; 2103 s->mEndWord = s->mWordCount - 1; 2104 s->mProgress = ZIP_CODE; 2105 // a couple of delimiters is an indication that the state name is good 2106 // or, a non-space / non-alpha-digit is also good 2107 s->mZipDelimiter = s->mLineCount > 2 2108 || isUnicodeSpace(ch) == false 2109 || chars == s->mEnd; 2110 if (WTF::isASCIIDigit(ch)) 2111 s->mZipStart = chars; 2112 goto resetState; 2113 case ZIP_CODE: 2114 if (WTF::isASCIIDigit(ch)) { 2115 int count = ++s->mNumberCount; 2116 if (count == 1) { 2117 if (WTF::isASCIIDigit(prior)) 2118 ++s->mNumberCount; 2119 else 2120 s->mZipStart = chars; 2121 } 2122 if (count <= 9) 2123 continue; 2124 } else if (isUnicodeSpace(ch)) { 2125 if (s->mNumberCount == 0) { 2126 s->mZipDelimiter = true; // two spaces delimit state name 2127 continue; 2128 } 2129 } else if (ch == '-') { 2130 if (s->mNumberCount == 5 && validZip(s->mZipHint, s->mZipStart)) { 2131 s->mNumberCount = 0; 2132 s->mProgress = PLUS_4; 2133 continue; 2134 } 2135 if (s->mNumberCount == 0) 2136 s->mZipDelimiter = true; 2137 } else if (WTF::isASCIIAlpha(ch) == false) 2138 s->mZipDelimiter = true; 2139 else { 2140 if (s->mLetterCount == 0) { 2141 s->newWord(baseChars, chars); 2142 s->mUnparsed = true; 2143 } 2144 ++s->mLetterCount; 2145 } 2146 if (s->mNumberCount == 5 || s->mNumberCount == 9) { 2147 if (validZip(s->mZipHint, s->mZipStart) == false) 2148 goto noZipMatch; 2149 s->mEndResult = chars - s->mCurrentStart; 2150 s->mEndWord = s->mWordCount - 1; 2151 } else if (s->mZipDelimiter == false) { 2152 noZipMatch: 2153 --chars; 2154 s->mProgress = ADDRESS_LINE; 2155 continue; 2156 } 2157 s->mProgress = FIND_STREET; 2158 goto findStreet; 2159 case PLUS_4: 2160 if (WTF::isASCIIDigit(ch)) { 2161 if (++s->mNumberCount <= 4) 2162 continue; 2163 } 2164 if (isUnicodeSpace(ch)) { 2165 if (s->mNumberCount == 0) 2166 continue; 2167 } 2168 if (s->mNumberCount == 4) { 2169 if (WTF::isASCIIAlpha(ch) == false) { 2170 s->mEndResult = chars - s->mCurrentStart; 2171 s->mEndWord = s->mWordCount - 1; 2172 } 2173 } else if (s->mNumberCount != 0) 2174 break; 2175 s->mProgress = FIND_STREET; 2176 case FIND_STREET: 2177 findStreet: 2178 retryState = false; 2179 for (int wordsIndex = s->mStateWord - 1; wordsIndex >= 0; --wordsIndex) { 2180 const UChar* test = s->mWords[wordsIndex]; 2181 UChar letter = test[0]; 2182 letter -= 'A'; 2183 if (letter > 'X' - 'A') 2184 continue; 2185 const char* names = longStreetNames[letter]; 2186 if (names == NULL) 2187 continue; 2188 int offset; 2189 while ((offset = *names++) != 0) { 2190 int testIndex = 1; 2191 bool abbr = false; 2192 for (int idx = 0; idx < offset; idx++) { 2193 char nameLetter = names[idx]; 2194 char testUpper = WTF::toASCIIUpper(test[testIndex]); 2195 if (nameLetter == '*') { 2196 if (testUpper == 'S') 2197 testIndex++; 2198 break; 2199 } 2200 bool fullOnly = WTF::isASCIILower(nameLetter); 2201 nameLetter = WTF::toASCIIUpper(nameLetter); 2202 if (testUpper == nameLetter) { 2203 if (abbr && fullOnly) 2204 goto nextTest; 2205 testIndex++; 2206 continue; 2207 } 2208 if (fullOnly == false) 2209 goto nextTest; 2210 abbr = true; 2211 } 2212 letter = &test[testIndex] < s->mEnds[wordsIndex] ? 2213 test[testIndex] : ' '; 2214 if (WTF::isASCIIAlpha(letter) == false && WTF::isASCIIDigit(letter) == false) { 2215 if (s->mNumberWords != 0) { 2216 int shift = 0; 2217 int wordReduction = -1; 2218 do { 2219 while ((s->mNumberWords & (1 << shift)) == 0) 2220 shift++; 2221 if (shift > wordsIndex) 2222 break; 2223 wordReduction = shift; 2224 } while (s->mNumberWords >> ++shift != 0); 2225 if (wordReduction >= 0) { 2226 if (s->mContinuationNode) { 2227 if (retryState) 2228 break; 2229 return FOUND_NONE; 2230 } 2231 s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction]; 2232 } 2233 } 2234 if (wordsIndex != s->mStateWord - 1) 2235 return FOUND_COMPLETE; 2236 retryState = true; 2237 } 2238 nextTest: 2239 names += offset; 2240 } 2241 } 2242 if (retryState) { 2243 s->mProgress = ADDRESS_LINE; 2244 s->mStates = NULL; 2245 continue; 2246 } 2247 if (s->mNumberWords != 0) { 2248 unsigned shift = 0; 2249 while ((s->mNumberWords & (1 << shift)) == 0) 2250 shift++; 2251 s->mNumberWords >>= ++shift; 2252 if (s->mBases[0] != s->mBases[shift]) 2253 return FOUND_NONE; 2254 s->shiftWords(shift); 2255 s->mStartResult = s->mWords[0] - s->mStarts[0]; 2256 s->mWordCount -= shift; 2257 s->mProgress = ADDRESS_LINE; 2258 --chars; 2259 continue; 2260 } 2261 break; 2262 } 2263 if (s->mContinuationNode) 2264 return FOUND_NONE; 2265 s->mProgress = NO_ADDRESS; 2266 s->mWordCount = s->mLineCount = 0; 2267 s->mNumberWords = 0; 2268 resetState: 2269 s->mStates = NULL; 2270 resetWord: 2271 s->mNumberCount = s->mLetterCount = 0; 2272 s->mFirstLower = NULL; 2273 s->mUnparsed = false; 2274 } 2275 s->mCurrent = ch; 2276 return s->mProgress == NO_ADDRESS ? FOUND_NONE : FOUND_PARTIAL; 2277 } 2278 2279 // Recogize common email patterns only. Currently has lots of state, walks text forwards and backwards -- will be 2280 // a real challenge to adapt to walk text across multiple nodes, I imagine 2281 // FIXME: it's too hard for the caller to call these incrementally -- it's probably best for this to 2282 // either walk the node tree directly or make a callout to get the next or previous node, if there is one 2283 // walking directly will avoid adding logic in caller to track the multiple partial or full nodes that compose this 2284 // text pattern. 2285 CacheBuilder::FoundState CacheBuilder::FindPartialEMail(const UChar* chars, unsigned length, 2286 FindState* s) 2287 { 2288 // the following tables were generated by tests/browser/focusNavigation/BrowserDebug.cpp 2289 // hand-edit at your own risk 2290 static const int domainTwoLetter[] = { 2291 0x02df797c, // a followed by: [cdefgilmnoqrstuwxz] 2292 0x036e73fb, // b followed by: [abdefghijmnorstvwyz] 2293 0x03b67ded, // c followed by: [acdfghiklmnorsuvxyz] 2294 0x02005610, // d followed by: [ejkmoz] 2295 0x001e00d4, // e followed by: [ceghrstu] 2296 0x00025700, // f followed by: [ijkmor] 2297 0x015fb9fb, // g followed by: [abdefghilmnpqrstuwy] 2298 0x001a3400, // h followed by: [kmnrtu] 2299 0x000f7818, // i followed by: [delmnoqrst] 2300 0x0000d010, // j followed by: [emop] 2301 0x0342b1d0, // k followed by: [eghimnprwyz] 2302 0x013e0507, // l followed by: [abcikrstuvy] 2303 0x03fffccd, // m followed by: [acdghklmnopqrstuvwxyz] 2304 0x0212c975, // n followed by: [acefgilopruz] 2305 0x00001000, // o followed by: [m] 2306 0x014e3cf1, // p followed by: [aefghklmnrstwy] 2307 0x00000001, // q followed by: [a] 2308 0x00504010, // r followed by: [eouw] 2309 0x032a7fdf, // s followed by: [abcdeghijklmnortvyz] 2310 0x026afeec, // t followed by: [cdfghjklmnoprtvwz] 2311 0x03041441, // u followed by: [agkmsyz] 2312 0x00102155, // v followed by: [aceginu] 2313 0x00040020, // w followed by: [fs] 2314 0x00000000, // x 2315 0x00180010, // y followed by: [etu] 2316 0x00401001, // z followed by: [amw] 2317 }; 2318 2319 static char const* const longDomainNames[] = { 2320 "\x03" "ero" "\x03" "rpa", // aero, arpa 2321 "\x02" "iz", // biz 2322 "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop 2323 NULL, // d 2324 "\x02" "du", // edu 2325 NULL, // f 2326 "\x02" "ov", // gov 2327 NULL, // h 2328 "\x03" "nfo" "\x02" "nt", // info, int 2329 "\x03" "obs", // jobs 2330 NULL, // k 2331 NULL, // l 2332 "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum 2333 "\x03" "ame" "\x02" "et", // name, net 2334 "\x02" "rg", // , org 2335 "\x02" "ro", // pro 2336 NULL, // q 2337 NULL, // r 2338 NULL, // s 2339 "\x05" "ravel", // travel 2340 NULL, // u 2341 NULL, // v 2342 NULL, // w 2343 NULL, // x 2344 NULL, // y 2345 NULL, // z 2346 }; 2347 2348 const UChar* start = chars; 2349 const UChar* end = chars + length; 2350 while (chars < end) { 2351 UChar ch = *chars++; 2352 if (ch != '@') 2353 continue; 2354 const UChar* atLocation = chars - 1; 2355 // search for domain 2356 ch = *chars++ | 0x20; // convert uppercase to lower 2357 if (ch < 'a' || ch > 'z') 2358 continue; 2359 while (chars < end) { 2360 ch = *chars++; 2361 if (IsDomainChar(ch) == false) 2362 goto nextAt; 2363 if (ch != '.') 2364 continue; 2365 UChar firstLetter = *chars++ | 0x20; // first letter of the domain 2366 if (chars >= end) 2367 return FOUND_NONE; // only one letter; must be at least two 2368 firstLetter -= 'a'; 2369 if (firstLetter > 'z' - 'a') 2370 continue; // non-letter followed '.' 2371 int secondLetterMask = domainTwoLetter[firstLetter]; 2372 ch = *chars | 0x20; // second letter of the domain 2373 ch -= 'a'; 2374 if (ch >= 'z' - 'a') 2375 continue; 2376 bool secondMatch = (secondLetterMask & 1 << ch) != 0; 2377 const char* wordMatch = longDomainNames[firstLetter]; 2378 int wordIndex = 0; 2379 while (wordMatch != NULL) { 2380 int len = *wordMatch++; 2381 char match; 2382 do { 2383 match = wordMatch[wordIndex]; 2384 if (match < 0x20) 2385 goto foundDomainStart; 2386 if (chars[wordIndex] != match) 2387 break; 2388 wordIndex++; 2389 } while (true); 2390 wordMatch += len; 2391 if (*wordMatch == '\0') 2392 break; 2393 wordIndex = 0; 2394 } 2395 if (secondMatch) { 2396 wordIndex = 1; 2397 foundDomainStart: 2398 chars += wordIndex; 2399 if (chars < end) { 2400 ch = *chars; 2401 if (ch != '.') { 2402 if (IsDomainChar(ch)) 2403 goto nextDot; 2404 } else if (chars + 1 < end && IsDomainChar(chars[1])) 2405 goto nextDot; 2406 } 2407 // found domain. Search backwards from '@' for beginning of email address 2408 s->mEndResult = chars - start; 2409 chars = atLocation; 2410 if (chars <= start) 2411 goto nextAt; 2412 ch = *--chars; 2413 if (ch == '.') 2414 goto nextAt; // mailbox can't end in period 2415 do { 2416 if (IsMailboxChar(ch) == false) { 2417 chars++; 2418 break; 2419 } 2420 if (chars == start) 2421 break; 2422 ch = *--chars; 2423 } while (true); 2424 UChar firstChar = *chars; 2425 if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty 2426 goto nextAt; 2427 s->mStartResult = chars - start; 2428 return FOUND_COMPLETE; 2429 } 2430 nextDot: 2431 ; 2432 } 2433 nextAt: 2434 chars = atLocation + 1; 2435 } 2436 return FOUND_NONE; 2437 } 2438 2439 #define PHONE_PATTERN "(200) /-.\\ 100 -. 0000" // poor man's regex: parens optional, any one of punct, digit smallest allowed 2440 2441 CacheBuilder::FoundState CacheBuilder::FindPartialNumber(const UChar* chars, unsigned length, 2442 FindState* s) 2443 { 2444 char* pattern = s->mPattern; 2445 UChar* store = s->mStorePtr; 2446 const UChar* start = chars; 2447 const UChar* end = chars + length; 2448 const UChar* lastDigit = NULL; 2449 do { 2450 bool initialized = s->mInitialized; 2451 while (chars < end) { 2452 if (initialized == false) { 2453 s->mBackTwo = s->mBackOne; 2454 s->mBackOne = s->mCurrent; 2455 } 2456 UChar ch = s->mCurrent = *chars; 2457 do { 2458 char patternChar = *pattern; 2459 switch (patternChar) { 2460 case '2': 2461 if (initialized == false) { 2462 s->mStartResult = chars - start; 2463 initialized = true; 2464 } 2465 case '0': 2466 case '1': 2467 if (ch < patternChar || ch > '9') 2468 goto resetPattern; 2469 *store++ = ch; 2470 pattern++; 2471 lastDigit = chars; 2472 goto nextChar; 2473 case '\0': 2474 if (WTF::isASCIIDigit(ch) == false) { 2475 *store = '\0'; 2476 goto checkMatch; 2477 } 2478 goto resetPattern; 2479 case ' ': 2480 if (ch == patternChar) 2481 goto nextChar; 2482 break; 2483 case '(': 2484 if (ch == patternChar) { 2485 s->mStartResult = chars - start; 2486 initialized = true; 2487 s->mOpenParen = true; 2488 } 2489 goto commonPunctuation; 2490 case ')': 2491 if ((ch == patternChar) ^ s->mOpenParen) 2492 goto resetPattern; 2493 default: 2494 commonPunctuation: 2495 if (ch == patternChar) { 2496 pattern++; 2497 goto nextChar; 2498 } 2499 } 2500 } while (++pattern); // never false 2501 nextChar: 2502 chars++; 2503 } 2504 break; 2505 resetPattern: 2506 if (s->mContinuationNode) 2507 return FOUND_NONE; 2508 FindResetNumber(s); 2509 pattern = s->mPattern; 2510 store = s->mStorePtr; 2511 } while (++chars < end); 2512 checkMatch: 2513 if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo)) 2514 return FOUND_NONE; 2515 *store = '\0'; 2516 s->mStorePtr = store; 2517 s->mPattern = pattern; 2518 s->mEndResult = lastDigit - start + 1; 2519 char pState = pattern[0]; 2520 return pState == '\0' ? FOUND_COMPLETE : pState == '(' || (WTF::isASCIIDigit(pState) && WTF::isASCIIDigit(pattern[-1])) ? 2521 FOUND_NONE : FOUND_PARTIAL; 2522 } 2523 2524 CacheBuilder::FoundState CacheBuilder::FindPhoneNumber(const UChar* chars, unsigned length, 2525 int* start, int* end) 2526 { 2527 FindState state; 2528 FindReset(&state); 2529 FoundState result = FindPartialNumber(chars, length, &state); 2530 *start = state.mStartResult; 2531 *end = state.mEndResult; 2532 return result; 2533 } 2534 2535 void CacheBuilder::FindReset(FindState* state) 2536 { 2537 memset(state, 0, sizeof(FindState)); 2538 state->mCurrent = ' '; 2539 FindResetNumber(state); 2540 } 2541 2542 void CacheBuilder::FindResetNumber(FindState* state) 2543 { 2544 state->mOpenParen = false; 2545 state->mPattern = (char*) PHONE_PATTERN; 2546 state->mStorePtr = state->mStore; 2547 } 2548 2549 IntRect CacheBuilder::getAreaRect(const HTMLAreaElement* area) 2550 { 2551 Node* node = area->document(); 2552 while ((node = node->traverseNextNode()) != NULL) { 2553 RenderObject* renderer = node->renderer(); 2554 if (renderer && renderer->isRenderImage()) { 2555 RenderImage* image = static_cast<RenderImage*>(renderer); 2556 HTMLMapElement* map = image->imageMap(); 2557 if (map) { 2558 Node* n; 2559 for (n = map->firstChild(); n; 2560 n = n->traverseNextNode(map)) { 2561 if (n == area) { 2562 if (area->isDefault()) 2563 return image->absoluteBoundingBoxRect(); 2564 return area->computeRect(image); 2565 } 2566 } 2567 } 2568 } 2569 } 2570 return IntRect(); 2571 } 2572 2573 void CacheBuilder::GetGlobalOffset(Node* node, int* x, int * y) 2574 { 2575 GetGlobalOffset(node->document()->frame(), x, y); 2576 } 2577 2578 void CacheBuilder::GetGlobalOffset(Frame* frame, int* x, int* y) 2579 { 2580 // TIMER_PROBE(__FUNCTION__); 2581 ASSERT(x); 2582 ASSERT(y); 2583 *x = 0; 2584 *y = 0; 2585 if (!frame->view()) 2586 return; 2587 Frame* parent; 2588 while ((parent = frame->tree()->parent()) != NULL) { 2589 const WebCore::IntRect& rect = frame->view()->platformWidget()->getBounds(); 2590 *x += rect.x(); 2591 *y += rect.y(); 2592 frame = parent; 2593 } 2594 // TIMER_PROBE_END(); 2595 } 2596 2597 Frame* CacheBuilder::HasFrame(Node* node) 2598 { 2599 RenderObject* renderer = node->renderer(); 2600 if (renderer == NULL) 2601 return NULL; 2602 if (renderer->isWidget() == false) 2603 return NULL; 2604 Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); 2605 if (widget == NULL) 2606 return NULL; 2607 if (widget->isFrameView() == false) 2608 return NULL; 2609 return static_cast<FrameView*>(widget)->frame(); 2610 } 2611 2612 bool CacheBuilder::HasOverOrOut(Node* node) 2613 { 2614 // eventNames are thread-local data, I avoid using 'static' variable here. 2615 AtomicString eventTypes[2] = { 2616 eventNames().mouseoverEvent, 2617 eventNames().mouseoutEvent 2618 }; 2619 2620 return NodeHasEventListeners(node, eventTypes, 2); 2621 } 2622 2623 bool CacheBuilder::HasTriggerEvent(Node* node) 2624 { 2625 AtomicString eventTypes[5] = { 2626 eventNames().clickEvent, 2627 eventNames().mousedownEvent, 2628 eventNames().mouseupEvent, 2629 eventNames().keydownEvent, 2630 eventNames().keyupEvent 2631 }; 2632 2633 return NodeHasEventListeners(node, eventTypes, 5); 2634 } 2635 2636 // #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 2637 // - 0x2D . 0x2E 0-9 0x30-39 A-Z 0x41-5A _ 0x5F a-z 0x61-7A 2638 2639 bool CacheBuilder::IsDomainChar(UChar ch) 2640 { 2641 static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z 2642 ch -= 0x20; 2643 if (ch > 'z' - 0x20) 2644 return false; 2645 return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; 2646 } 2647 2648 bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node, 2649 CachedNodeType* type, String* exported) const 2650 { 2651 Text* textNode = static_cast<Text*>(node); 2652 StringImpl* string = textNode->dataImpl(); 2653 const UChar* baseChars = string->characters(); 2654 // const UChar* originalBase = baseChars; 2655 int length = string->length(); 2656 int index = 0; 2657 while (index < length && isUnicodeSpace(baseChars[index])) 2658 index++; 2659 if (index >= length) 2660 return false; 2661 if (more == false) { 2662 walk->mStart = 0; 2663 walk->mEnd = 0; 2664 walk->mFinalNode = node; 2665 walk->mLastInline = NULL; 2666 } 2667 // starting with this node, search forward for email, phone number, and address 2668 // if any of the three is found, track it so that the remaining can be looked for later 2669 FoundState state = FOUND_NONE; 2670 RenderText* renderer = (RenderText*) node->renderer(); 2671 bool foundBetter = false; 2672 InlineTextBox* baseInline = walk->mLastInline != NULL ? walk->mLastInline : 2673 renderer->firstTextBox(); 2674 if (baseInline == NULL) 2675 return false; 2676 int start = walk->mEnd; 2677 InlineTextBox* saveInline; 2678 int baseStart, firstStart = start; 2679 saveInline = baseInline; 2680 baseStart = start; 2681 for (CachedNodeType checkType = ADDRESS_CACHEDNODETYPE; 2682 checkType <= PHONE_CACHEDNODETYPE; 2683 checkType = static_cast<CachedNodeType>(checkType + 1)) 2684 { 2685 if ((1 << (checkType - 1) & mAllowableTypes) == 0) 2686 continue; 2687 InlineTextBox* inlineTextBox = baseInline; 2688 FindState findState; 2689 FindReset(&findState); 2690 start = baseStart; 2691 if (checkType == ADDRESS_CACHEDNODETYPE) { 2692 findState.mBases[0] = baseChars; 2693 findState.mWords[0] = baseChars + start; 2694 findState.mStarts[0] = baseChars + start; 2695 } 2696 Node* lastPartialNode = NULL; 2697 int lastPartialEnd = -1; 2698 bool lastPartialMore = false; 2699 bool firstPartial = true; 2700 InlineTextBox* lastPartialInline = NULL; 2701 do { 2702 do { 2703 const UChar* chars = baseChars + start; 2704 length = inlineTextBox == NULL ? 0 : 2705 inlineTextBox->end() - start + 1; 2706 bool wasInitialized = findState.mInitialized; 2707 switch (checkType) { 2708 case ADDRESS_CACHEDNODETYPE: 2709 state = FindPartialAddress(baseChars, chars, length, &findState); 2710 break; 2711 case EMAIL_CACHEDNODETYPE: 2712 state = FindPartialEMail(chars, length, &findState); 2713 break; 2714 case PHONE_CACHEDNODETYPE: 2715 state = FindPartialNumber(chars, length, &findState); 2716 break; 2717 default: 2718 ASSERT(0); 2719 } 2720 findState.mInitialized = state != FOUND_NONE; 2721 if (wasInitialized != findState.mInitialized) 2722 firstStart = start; 2723 if (state == FOUND_PARTIAL) { 2724 lastPartialNode = node; 2725 lastPartialEnd = findState.mEndResult + start; 2726 lastPartialMore = firstPartial && 2727 lastPartialEnd < (int) string->length(); 2728 firstPartial = false; 2729 lastPartialInline = inlineTextBox; 2730 findState.mContinuationNode = true; 2731 } else if (state == FOUND_COMPLETE) { 2732 if (foundBetter == false || walk->mStart > findState.mStartResult) { 2733 walk->mStart = findState.mStartResult + firstStart; 2734 if (findState.mEndResult > 0) { 2735 walk->mFinalNode = node; 2736 walk->mEnd = findState.mEndResult + start; 2737 walk->mMore = node == textNode && 2738 walk->mEnd < (int) string->length(); 2739 walk->mLastInline = inlineTextBox; 2740 } else { 2741 walk->mFinalNode = lastPartialNode; 2742 walk->mEnd = lastPartialEnd; 2743 walk->mMore = lastPartialMore; 2744 walk->mLastInline = lastPartialInline; 2745 } 2746 *type = checkType; 2747 if (checkType == PHONE_CACHEDNODETYPE) { 2748 const UChar* store = findState.mStore; 2749 *exported = String(store); 2750 } else { 2751 Node* temp = textNode; 2752 length = 1; 2753 start = walk->mStart; 2754 exported->truncate(0); 2755 do { 2756 Text* tempText = static_cast<Text*>(temp); 2757 StringImpl* string = tempText->dataImpl(); 2758 int end = tempText == walk->mFinalNode ? 2759 walk->mEnd : string->length(); 2760 exported->append(String(string->substring( 2761 start, end - start))); 2762 ASSERT(end > start); 2763 length += end - start + 1; 2764 if (temp == walk->mFinalNode) 2765 break; 2766 start = 0; 2767 do { 2768 temp = temp->traverseNextNode(); 2769 ASSERT(temp); 2770 } while (temp->isTextNode() == false); 2771 // add a space in between text nodes to avoid 2772 // words collapsing together 2773 exported->append(" "); 2774 } while (true); 2775 } 2776 foundBetter = true; 2777 } 2778 goto tryNextCheckType; 2779 } else if (findState.mContinuationNode) 2780 break; 2781 if (inlineTextBox == NULL) 2782 break; 2783 inlineTextBox = inlineTextBox->nextTextBox(); 2784 if (inlineTextBox == NULL) 2785 break; 2786 start = inlineTextBox->start(); 2787 if (state == FOUND_PARTIAL && node == textNode) 2788 findState.mContinuationNode = false; 2789 } while (true); 2790 if (state == FOUND_NONE) 2791 break; 2792 // search for next text node, if any 2793 Text* nextNode; 2794 do { 2795 do { 2796 do { 2797 if (node) 2798 node = node->traverseNextNode(); 2799 if (node == NULL || node->hasTagName(HTMLNames::aTag) 2800 || node->hasTagName(HTMLNames::inputTag) 2801 || node->hasTagName(HTMLNames::textareaTag)) { 2802 if (state == FOUND_PARTIAL && 2803 checkType == ADDRESS_CACHEDNODETYPE && 2804 findState.mProgress == ZIP_CODE && 2805 findState.mNumberCount == 0) { 2806 baseChars = NULL; 2807 inlineTextBox = NULL; 2808 start = 0; 2809 findState.mProgress = FIND_STREET; 2810 goto finalNode; 2811 } 2812 goto tryNextCheckType; 2813 } 2814 } while (node->isTextNode() == false); 2815 nextNode = static_cast<Text*>(node); 2816 renderer = (RenderText*) nextNode->renderer(); 2817 } while (renderer == NULL); 2818 baseInline = renderer->firstTextBox(); 2819 } while (baseInline == NULL); 2820 string = nextNode->dataImpl(); 2821 baseChars = string->characters(); 2822 inlineTextBox = baseInline; 2823 start = inlineTextBox->start(); 2824 finalNode: 2825 findState.mEndResult = 0; 2826 } while (true); 2827 tryNextCheckType: 2828 node = textNode; 2829 baseInline = saveInline; 2830 string = textNode->dataImpl(); 2831 baseChars = string->characters(); 2832 } 2833 if (foundBetter) { 2834 CachedNodeType temp = *type; 2835 switch (temp) { 2836 case ADDRESS_CACHEDNODETYPE: { 2837 static const char geoString[] = "geo:0,0?q="; 2838 exported->insert(String(geoString), 0); 2839 int index = sizeof(geoString) - 1; 2840 String escapedComma("%2C"); 2841 while ((index = exported->find(',', index)) >= 0) 2842 exported->replace(index, 1, escapedComma); 2843 } break; 2844 case EMAIL_CACHEDNODETYPE: { 2845 String encoded = WebCore::encodeWithURLEscapeSequences(*exported); 2846 exported->swap(encoded); 2847 exported->insert(WTF::String("mailto:"), 0); 2848 } break; 2849 case PHONE_CACHEDNODETYPE: 2850 exported->insert(WTF::String("tel:"), 0); 2851 break; 2852 default: 2853 break; 2854 } 2855 return true; 2856 } 2857 noTextMatch: 2858 walk->reset(); 2859 return false; 2860 } 2861 2862 bool CacheBuilder::IsMailboxChar(UChar ch) 2863 { 2864 // According to http://en.wikipedia.org/wiki/Email_address 2865 // ! # $ % & ' * + - . / 0-9 = ? 2866 // A-Z ^ _ 2867 // ` a-z { | } ~ 2868 static const unsigned body[] = {0xa3ffecfa, 0xc7fffffe, 0x7fffffff}; 2869 ch -= 0x20; 2870 if (ch > '~' - 0x20) 2871 return false; 2872 return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0; 2873 } 2874 2875 bool CacheBuilder::setData(CachedFrame* cachedFrame) 2876 { 2877 Frame* frame = FrameAnd(this); 2878 Document* doc = frame->document(); 2879 if (doc == NULL) 2880 return false; 2881 RenderObject* renderer = doc->renderer(); 2882 if (renderer == NULL) 2883 return false; 2884 RenderLayer* layer = renderer->enclosingLayer(); 2885 if (layer == NULL) 2886 return false; 2887 if (layer->width() == 0 || layer->height() == 0) 2888 return false; 2889 if (!frame->view()) 2890 return false; 2891 int x, y; 2892 GetGlobalOffset(frame, &x, &y); 2893 WebCore::IntRect viewBounds = frame->view()->platformWidget()->getBounds(); 2894 if ((x | y) != 0) 2895 viewBounds.setLocation(WebCore::IntPoint(x, y)); 2896 cachedFrame->setLocalViewBounds(viewBounds); 2897 cachedFrame->setContentsSize(layer->scrollWidth(), layer->scrollHeight()); 2898 if (cachedFrame->childCount() == 0) 2899 return true; 2900 CachedFrame* lastCachedFrame = cachedFrame->lastChild(); 2901 cachedFrame = cachedFrame->firstChild(); 2902 do { 2903 CacheBuilder* cacheBuilder = Builder((Frame* )cachedFrame->framePointer()); 2904 cacheBuilder->setData(cachedFrame); 2905 } while (cachedFrame++ != lastCachedFrame); 2906 return true; 2907 } 2908 2909 #if USE(ACCELERATED_COMPOSITING) 2910 void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker, 2911 RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY) 2912 { 2913 RenderLayer* layer = nodeRenderer->enclosingLayer(); 2914 RenderLayerBacking* back = layer->backing(); 2915 if (!back) 2916 return; 2917 GraphicsLayer* grLayer = back->graphicsLayer(); 2918 if (back->hasContentsLayer()) 2919 grLayer = back->foregroundLayer(); 2920 if (!grLayer) 2921 return; 2922 LayerAndroid* aLayer = grLayer->platformLayer(); 2923 if (!aLayer) 2924 return; 2925 IntPoint scroll(layer->scrollXOffset(), layer->scrollYOffset()); 2926 #if ENABLE(ANDROID_OVERFLOW_SCROLL) 2927 // If this is an overflow element, track the content layer. 2928 if (layer->hasOverflowScroll() && aLayer->getChild(0)) 2929 aLayer = aLayer->getChild(0)->getChild(0); 2930 if (!aLayer) 2931 return; 2932 // Prevent a crash when scrolling a layer that does not have a parent. 2933 if (layer->stackingContext()) 2934 layer->scrollToOffset(0, 0); 2935 #endif 2936 layerTracker.grow(layerTracker.size() + 1); 2937 LayerTracker& indexTracker = layerTracker.last(); 2938 indexTracker.mLayer = aLayer; 2939 indexTracker.mRenderLayer = layer; 2940 indexTracker.mBounds = enclosingIntRect(aLayer->bounds()); 2941 // Use the absolute location of the layer as the bounds location. This 2942 // provides the original offset of nodes in the layer so that we can 2943 // translate nodes between their original location and the layer's new 2944 // location. 2945 indexTracker.mBounds.setLocation(layer->absoluteBoundingBox().location()); 2946 indexTracker.mBounds.move(offsetX, offsetY); 2947 indexTracker.mScroll = scroll; 2948 indexTracker.mLastChild = OneAfter(lastChild); 2949 DBG_NAV_LOGD("layer=%p [%d] bounds=(%d,%d,w=%d,h=%d)", aLayer, 2950 aLayer->uniqueId(), indexTracker.mBounds.x(), indexTracker.mBounds.y(), 2951 indexTracker.mBounds.width(), indexTracker.mBounds.height()); 2952 } 2953 #endif 2954 2955 bool CacheBuilder::validNode(Frame* startFrame, void* matchFrame, 2956 void* matchNode) 2957 { 2958 if (matchFrame == startFrame) { 2959 if (matchNode == NULL) 2960 return true; 2961 Node* node = startFrame->document(); 2962 while (node != NULL) { 2963 if (node == matchNode) { 2964 const IntRect& rect = node->hasTagName(HTMLNames::areaTag) ? 2965 getAreaRect(static_cast<HTMLAreaElement*>(node)) : node->getRect(); 2966 // Consider nodes with empty rects that are not at the origin 2967 // to be valid, since news.google.com has valid nodes like this 2968 if (rect.x() == 0 && rect.y() == 0 && rect.isEmpty()) 2969 return false; 2970 return true; 2971 } 2972 node = node->traverseNextNode(); 2973 } 2974 DBG_NAV_LOGD("frame=%p valid node=%p invalid\n", matchFrame, matchNode); 2975 return false; 2976 } 2977 Frame* child = startFrame->tree()->firstChild(); 2978 while (child) { 2979 bool result = validNode(child, matchFrame, matchNode); 2980 if (result) 2981 return result; 2982 child = child->tree()->nextSibling(); 2983 } 2984 #if DEBUG_NAV_UI 2985 if (startFrame->tree()->parent() == NULL) 2986 DBG_NAV_LOGD("frame=%p node=%p false\n", matchFrame, matchNode); 2987 #endif 2988 return false; 2989 } 2990 2991 static int Area(const IntRect& rect) 2992 { 2993 return rect.width() * rect.height(); 2994 } 2995 2996 bool CacheBuilder::AddPartRect(IntRect& bounds, int x, int y, 2997 WTF::Vector<IntRect>* result, IntRect* focusBounds) 2998 { 2999 if (bounds.isEmpty()) 3000 return true; 3001 bounds.move(x, y); 3002 if (bounds.maxX() <= 0 || bounds.maxY() <= 0) 3003 return true; 3004 IntRect* work = result->begin() - 1; 3005 IntRect* end = result->end(); 3006 while (++work < end) { 3007 if (work->contains(bounds)) 3008 return true; 3009 if (bounds.contains(*work)) { 3010 *work = bounds; 3011 focusBounds->unite(bounds); 3012 return true; 3013 } 3014 if ((bounds.x() != work->x() || bounds.width() != work->width()) && 3015 (bounds.y() != work->y() || bounds.height() != work->height())) 3016 continue; 3017 IntRect test = *work; 3018 test.unite(bounds); 3019 if (Area(test) > Area(*work) + Area(bounds)) 3020 continue; 3021 *work = test; 3022 focusBounds->unite(bounds); 3023 return true; 3024 } 3025 if (result->size() >= MAXIMUM_FOCUS_RING_COUNT) 3026 return false; 3027 result->append(bounds); 3028 if (focusBounds->isEmpty()) 3029 *focusBounds = bounds; 3030 else 3031 focusBounds->unite(bounds); 3032 return true; 3033 } 3034 3035 bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds, 3036 IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result, 3037 int* imageCountPtr) 3038 { 3039 WTF::Vector<ClipColumnTracker> clipTracker(1); 3040 ClipColumnTracker* baseTracker = clipTracker.data(); // sentinel 3041 bzero(baseTracker, sizeof(ClipColumnTracker)); 3042 if (node->hasChildNodes() && node->hasTagName(HTMLNames::buttonTag) == false 3043 && node->hasTagName(HTMLNames::selectTag) == false) { 3044 // collect all text rects from first to last child 3045 Node* test = node->firstChild(); 3046 Node* last = NULL; 3047 Node* prior = node; 3048 while ((prior = prior->lastChild()) != NULL) 3049 last = prior; 3050 ASSERT(last != NULL); 3051 bool nodeIsAnchor = node->hasTagName(HTMLNames::aTag); 3052 do { 3053 do { 3054 const ClipColumnTracker* lastClip = &clipTracker.last(); 3055 if (test != lastClip->mLastChild) 3056 break; 3057 clipTracker.removeLast(); 3058 } while (true); 3059 RenderObject* renderer = test->renderer(); 3060 if (renderer == NULL) 3061 continue; 3062 EVisibility vis = renderer->style()->visibility(); 3063 if (vis == HIDDEN) 3064 continue; 3065 bool hasClip = renderer->hasOverflowClip(); 3066 size_t clipIndex = clipTracker.size(); 3067 IntRect clipBounds = IntRect(0, 0, INT_MAX, INT_MAX); 3068 if (hasClip || --clipIndex > 0) { 3069 clipBounds = hasClip ? renderer->absoluteBoundingBoxRect() : 3070 clipTracker.at(clipIndex).mBounds; // x, y fixup done by ConstructTextRect 3071 } 3072 if (test->isTextNode()) { 3073 RenderText* renderText = (RenderText*) renderer; 3074 InlineTextBox *textBox = renderText->firstTextBox(); 3075 if (textBox == NULL) 3076 continue; 3077 if (ConstructTextRect((Text*) test, textBox, 0, INT_MAX, 3078 x, y, focusBounds, clipBounds, result) == false) { 3079 return false; 3080 } 3081 continue; 3082 } 3083 if (test->hasTagName(HTMLNames::imgTag)) { 3084 IntRect bounds = test->getRect(); 3085 bounds.intersect(clipBounds); 3086 if (AddPartRect(bounds, x, y, result, focusBounds) == false) 3087 return false; 3088 *imageCountPtr += 1; 3089 continue; 3090 } 3091 if (hasClip == false) { 3092 if (nodeIsAnchor && test->hasTagName(HTMLNames::divTag)) { 3093 IntRect bounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by AddPartRect 3094 RenderBox* renderBox = static_cast<RenderBox*>(renderer); 3095 int left = bounds.x() + renderBox->paddingLeft() + renderBox->borderLeft(); 3096 int top = bounds.y() + renderBox->paddingTop() + renderBox->borderTop(); 3097 int right = bounds.maxX() - renderBox->paddingRight() - renderBox->borderRight(); 3098 int bottom = bounds.maxY() - renderBox->paddingBottom() - renderBox->borderBottom(); 3099 if (left >= right || top >= bottom) 3100 continue; 3101 bounds = IntRect(left, top, right - left, bottom - top); 3102 if (AddPartRect(bounds, x, y, result, focusBounds) == false) 3103 return false; 3104 } 3105 continue; 3106 } 3107 Node* lastChild = test->lastChild(); 3108 if (lastChild == NULL) 3109 continue; 3110 clipTracker.grow(clipTracker.size() + 1); 3111 ClipColumnTracker& clip = clipTracker.last(); 3112 clip.mBounds = renderer->absoluteBoundingBoxRect(); // x, y fixup done by ConstructTextRect 3113 clip.mLastChild = OneAfter(lastChild); 3114 clip.mNode = test; 3115 } while (test != last && (test = test->traverseNextNode()) != NULL); 3116 } 3117 if (result->size() == 0 || focusBounds->width() < MINIMUM_FOCUSABLE_WIDTH 3118 || focusBounds->height() < MINIMUM_FOCUSABLE_HEIGHT) { 3119 if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH) 3120 return false; 3121 if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT) 3122 return false; 3123 result->append(bounds); 3124 *focusBounds = bounds; 3125 } 3126 return true; 3127 } 3128 3129 static inline bool isNotSpace(UChar c) 3130 { 3131 return c <= 0xA0 ? isUnicodeSpace(c) == false : 3132 WTF::Unicode::direction(c) != WTF::Unicode::WhiteSpaceNeutral; 3133 } 3134 3135 bool CacheBuilder::ConstructTextRect(Text* textNode, 3136 InlineTextBox* textBox, int start, int relEnd, int x, int y, 3137 IntRect* focusBounds, const IntRect& clipBounds, WTF::Vector<IntRect>* result) 3138 { 3139 RenderText* renderText = (RenderText*) textNode->renderer(); 3140 EVisibility vis = renderText->style()->visibility(); 3141 StringImpl* string = textNode->dataImpl(); 3142 const UChar* chars = string->characters(); 3143 FloatPoint pt = renderText->localToAbsolute(); 3144 do { 3145 int textBoxStart = textBox->start(); 3146 int textBoxEnd = textBoxStart + textBox->len(); 3147 if (textBoxEnd <= start) 3148 continue; 3149 if (textBoxEnd > relEnd) 3150 textBoxEnd = relEnd; 3151 IntRect bounds = textBox->selectionRect((int) pt.x(), (int) pt.y(), 3152 start, textBoxEnd); 3153 bounds.intersect(clipBounds); 3154 if (bounds.isEmpty()) 3155 continue; 3156 bool drawable = false; 3157 for (int index = start; index < textBoxEnd; index++) 3158 if ((drawable |= isNotSpace(chars[index])) != false) 3159 break; 3160 if (drawable && vis != HIDDEN) { 3161 if (AddPartRect(bounds, x, y, result, focusBounds) == false) 3162 return false; 3163 } 3164 if (textBoxEnd == relEnd) 3165 break; 3166 } while ((textBox = textBox->nextTextBox()) != NULL); 3167 return true; 3168 } 3169 3170 bool CacheBuilder::ConstructTextRects(Text* node, int start, 3171 Text* last, int end, int x, int y, IntRect* focusBounds, 3172 const IntRect& clipBounds, WTF::Vector<IntRect>* result) 3173 { 3174 result->clear(); 3175 *focusBounds = IntRect(0, 0, 0, 0); 3176 do { 3177 RenderText* renderText = (RenderText*) node->renderer(); 3178 int relEnd = node == last ? end : renderText->textLength(); 3179 InlineTextBox *textBox = renderText->firstTextBox(); 3180 if (textBox != NULL) { 3181 do { 3182 if ((int) textBox->end() >= start) 3183 break; 3184 } while ((textBox = textBox->nextTextBox()) != NULL); 3185 if (textBox && ConstructTextRect(node, textBox, start, relEnd, 3186 x, y, focusBounds, clipBounds, result) == false) 3187 return false; 3188 } 3189 start = 0; 3190 do { 3191 if (node == last) 3192 return true; 3193 node = (Text*) node->traverseNextNode(); 3194 ASSERT(node != NULL); 3195 } while (node->isTextNode() == false || node->renderer() == NULL); 3196 } while (true); 3197 } 3198 3199 } 3200