2 * Copyright 2006, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "CachedPrefix.h"
27 #include "CachedNode.h"
28 #include "CachedRoot.h"
29 #include "ColumnInfo.h"
31 #include "EventListener.h"
32 #include "EventNames.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"
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"
63 #include "WebCoreFrameBridge.h"
64 #include "WebCoreViewBridge.h"
66 #include <wtf/unicode/Unicode.h>
68 #ifdef DUMP_NAV_CACHE_USING_PRINTF
69 FILE* gNavCacheLogFile = NULL;
70 android::Mutex gWriteLogMutex;
73 #include "CacheBuilder.h"
75 #define MINIMUM_FOCUSABLE_WIDTH 3
76 #define MINIMUM_FOCUSABLE_HEIGHT 3
77 #define MAXIMUM_FOCUS_RING_COUNT 32
81 CacheBuilder* CacheBuilder::Builder(Frame* frame) {
82 return &((FrameLoaderClientAndroid*) frame->loader()->client())->getCacheBuilder();
85 Frame* CacheBuilder::FrameAnd(CacheBuilder* cacheBuilder) {
86 FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*)
87 ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder));
88 return loader->getFrame();
91 Frame* CacheBuilder::FrameAnd(const CacheBuilder* cacheBuilder) {
92 FrameLoaderClientAndroid* loader = (FrameLoaderClientAndroid*)
93 ((char*) cacheBuilder - OFFSETOF(FrameLoaderClientAndroid, m_cacheBuilder));
94 return loader->getFrame();
97 CacheBuilder::LayerTracker::~LayerTracker() {
98 // Check for a stacking context to prevent a crash in layers without a
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());
108 static bool hasEventListener(Node* node, const AtomicString& eventType) {
109 if (!node->isElementNode())
111 Element* element = static_cast<Element*>(node);
112 EventListener* listener = element->getAttributeEventListener(eventType);
113 return 0 != listener;
116 #define DEBUG_BUFFER_SIZE 256
117 #define DEBUG_WRAP_SIZE 150
118 #define DEBUG_WRAP_MAX 170
120 Frame* CacheBuilder::Debug::frameAnd() const {
121 CacheBuilder* nav = (CacheBuilder*) ((char*) this - OFFSETOF(CacheBuilder, mDebug));
122 return CacheBuilder::FrameAnd(nav);
125 void CacheBuilder::Debug::attr(const AtomicString& name, const AtomicString& value) {
126 if (name.isNull() || name.isEmpty() || value.isNull() || value.isEmpty())
128 uChar(name.characters(), name.length(), false);
130 wideString(value.characters(), value.length(), false);
134 void CacheBuilder::Debug::comma(const char* str) {
139 void CacheBuilder::Debug::flush() {
143 if (limit < DEBUG_WRAP_SIZE)
145 if (limit < DEBUG_WRAP_MAX)
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')
156 while (len > 0 && mBuffer[len - 1] == '\\')
158 while (mBuffer[len] == '"')
161 const char* prefix = mPrefix;
162 if (prefix[0] == '\"') {
163 // see if we're inside a quote
165 for (int index = 0; index < len; index++) {
166 if (mBuffer[index] == '\\') {
170 if (mBuffer[index] == '\n') {
174 if (mBuffer[index] == '"')
177 if ((quoteCount & 1) == 0)
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;
188 void CacheBuilder::Debug::frameName(char*& namePtr, const char* max) const {
191 Frame* frame = frameAnd();
192 Frame* parent = frame->tree()->parent();
194 Builder(parent)->mDebug.frameName(namePtr, max);
195 const AtomicString& name = frame->tree()->name();
196 if (name.length() == 0)
199 if (name.startsWith(AtomicString("opener")))
201 for (; index < name.length(); index++) {
202 char ch = name[index];
205 if (WTF::isASCIIAlphanumeric(ch) || ch == '_')
210 void CacheBuilder::Debug::frames() {
211 Frame* frame = frameAnd();
212 Document* doc = frame->document();
215 bool top = frame->tree()->parent() == NULL;
217 #ifdef DUMP_NAV_CACHE_USING_PRINTF
218 gWriteLogMutex.lock();
219 ASSERT(gNavCacheLogFile == NULL);
220 gNavCacheLogFile = fopen(NAV_CACHE_LOG_FILE, "a");
224 Frame* child = frame->tree()->firstChild();
225 bool hasChild = child != NULL;
227 DUMP_NAV_LOGD("\nnamespace TEST_SPACE {\n\n");
229 Builder(child)->mDebug.frames();
230 child = child->tree()->nextSibling();
233 child = frame->tree()->firstChild();
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();
244 RenderObject* renderer = doc->renderer();
245 if (renderer != NULL) {
246 RenderLayer* layer = renderer->enclosingLayer();
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();
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);
267 DUMP_NAV_LOGD("NULL, 0, ");
268 DUMP_NAV_LOGD("\"%s\"\n", childName);
269 DUMP_NAV_LOGD("}%c\n", next ? ',' : ' ');
275 DUMP_NAV_LOGD("};\n");
279 DUMP_NAV_LOGD("\n} // end of namespace\n\n");
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);
297 child = frame->tree()->firstChild();
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);
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();
317 void CacheBuilder::Debug::init(char* buffer, size_t size) {
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");
331 Builder(child)->mDebug.groups();
332 child = child->tree()->nextSibling();
334 if (frame->tree()->parent() == NULL && hasChild)
335 DUMP_NAV_LOGD("\n} // end of namespace\n\n");
336 Document* doc = frame->document();
338 char* frameNamePtr = name;
339 frameName(frameNamePtr, frameNamePtr + sizeof(name) - 1);
340 *frameNamePtr = '\0';
342 DUMP_NAV_LOGD("// %s has no document\n", name);
345 RenderObject* renderer = doc->renderer();
346 if (renderer == NULL) {
347 DUMP_NAV_LOGD("// %s has no renderer\n", name);
350 RenderLayer* layer = renderer->enclosingLayer();
352 DUMP_NAV_LOGD("// %s has no enclosingLayer\n", name);
356 Node* focus = doc->focusedNode();
357 bool atLeastOne = false;
359 if ((atLeastOne |= isFocusable(node)) != false)
361 } while ((node = node->traverseNextNode()) != NULL);
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"
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);
373 DUMP_NAV_LOGD("static DebugTestNode TEST%s_RECTS[] = {\n", name);
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 | ");
393 properties.append("FOCUS | ");
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");
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));
412 mBufferSize = sizeof(buffer);
414 mIndex = snprintf(buffer, sizeof(buffer), "{{%d, %d, %d, %d}, ", rect.x(), rect.y(),
415 rect.width(), rect.height());
417 uChar(properties.characters(), properties.length(), false);
419 int parentIndex = ParentIndex(node, count, node->parentNode());
421 snprintf(scratch, sizeof(scratch), "%d", parentIndex);
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);
435 wideString(node->textContent().characters(), node->textContent().length(), true);
436 } else if (node->hasTagName(HTMLNames::aTag) ||
437 node->hasTagName(HTMLNames::areaTag))
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());
446 RenderObject* renderer = node->renderer();
447 int tabindex = node->isElementNode() ? node->tabIndex() : 0;
448 RenderLayer* layer = 0;
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"
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()
462 print(", {0, 0, 0, 0}, false, 0},");
465 snprintf(scratch, sizeof(scratch), "// %d: ", count);
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();
476 for (unsigned i = 0; i < length; i++) {
477 Attribute* a = attrs->attributeItem(i);
478 attr(a->localName(), a->value());
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");
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());
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());
525 } while ((node = node->traverseNextNode()) != NULL);
526 DUMP_NAV_LOGD("}; // focusables = %d\n", count - 1);
528 DUMP_NAV_LOGD("static int TEST%s_RECT_COUNT = %d;\n\n", name, count - 1);
529 // look for rects with multiple parts
532 bool hasRectParts = false;
533 int globalOffsetX, globalOffsetY;
534 GetGlobalOffset(frame, &globalOffsetX, &globalOffsetY);
537 bool _isFocusable = isFocusable(node) || (node->isTextNode()
538 && node->getRect().isEmpty() == false
540 int nodeIndex = count++;
541 if (_isFocusable == false)
543 RenderObject* renderer = node->renderer();
544 if (renderer == NULL)
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;
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)
558 IntRect nodeBounds = node->getRect();
559 if (CacheBuilder::ConstructPartRects(node, nodeBounds, rectPtr,
560 globalOffsetX, globalOffsetY, &rects, &imageCount) == false)
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);
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);
579 RenderText* renderText = (RenderText*) node->renderer();
580 InlineTextBox* textBox = renderText->firstTextBox();
581 unsigned rectIndex = 0;
583 FloatPoint pt = renderText->localToAbsolute();
584 IntRect rect = textBox->selectionRect((int) pt.x(), (int) pt.y(), 0, INT_MAX);
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();
604 } while ((node = node->traverseNextNode()) != NULL);
606 DUMP_NAV_LOGD("{0}\n};\n\n");
608 DUMP_NAV_LOGD("static DebugTestRectPart* TEST%s_RECTPARTS = NULL;\n", name);
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);
617 bool CacheBuilder::Debug::isFocusable(Node* node) {
618 if (node->hasTagName(HTMLNames::areaTag))
620 if (node->renderer() == false)
622 if (node->isKeyboardFocusable(NULL))
624 if (node->isMouseFocusable())
626 if (node->isFocusable())
628 if (CacheBuilder::AnyIsClick(node))
630 if (CacheBuilder::HasTriggerEvent(node))
635 void CacheBuilder::Debug::localName(Node* node) {
636 const AtomicString& local = node->localName();
637 if (node->isTextNode())
640 wideString(local.characters(), local.length(), false);
644 void CacheBuilder::Debug::newLine(int indent) {
645 if (mPrefix[0] != '\n')
646 print(&mPrefix[0], 1);
648 int lastnewline = mIndex - 1;
649 while (lastnewline >= 0 && mBuffer[lastnewline] != '\n')
652 char* buffer = mBuffer;
653 if (lastnewline > 0) {
654 DUMP_NAV_LOGD("%.*s", lastnewline, buffer);
655 mIndex -= lastnewline;
656 buffer += lastnewline;
658 size_t prefixLen = strlen(mPrefix);
659 int minPrefix = prefixLen - 1;
660 while (minPrefix >= 0 && mPrefix[minPrefix] != '\n')
662 minPrefix = prefixLen - minPrefix - 1;
663 if (mIndex > minPrefix)
664 DUMP_NAV_LOGD("%.*s\n", mIndex, buffer);
669 int CacheBuilder::Debug::ParentIndex(Node* node, int count, Node* parent)
673 ASSERT(node != parent);
675 Node* previous = node;
678 previous = previous->traversePreviousNode();
679 } while (previous && previous != parent);
680 if (previous != NULL)
685 } while ((node = node->traverseNextNode()) != NULL && node != parent);
692 void CacheBuilder::Debug::print(const char* name) {
693 print(name, strlen(name));
696 void CacheBuilder::Debug::print(const char* name, unsigned len) {
698 if (mIndex + len >= DEBUG_BUFFER_SIZE)
700 int copyLen = mIndex + len < DEBUG_BUFFER_SIZE ?
701 len : DEBUG_BUFFER_SIZE - mIndex;
702 memcpy(&mBuffer[mIndex], name, copyLen);
707 mBuffer[mIndex] = '\0';
710 void CacheBuilder::Debug::setIndent(int indent)
713 snprintf(scratch, sizeof(scratch), "%.*s", indent,
718 void CacheBuilder::Debug::uChar(const UChar* name, unsigned len, bool hex) {
719 const UChar* end = name + len;
720 bool wroteHex = false;
722 unsigned ch = *name++;
723 if (ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0)
725 if (ch < ' ' || ch == 0x7f) {
727 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex, "\\x%02x", ch);
730 mBuffer[mIndex++] = '?';
731 } else if (ch >= 0x80) {
734 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex,
735 "\\x%02x\\x%02x", ch >> 6 | 0xc0, (ch & 0x3f) | 0x80);
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);
742 mBuffer[mIndex++] = '?';
744 if (wroteHex && WTF::isASCIIHexDigit((UChar) ch))
745 mIndex += snprintf(&mBuffer[mIndex], mBufferSize - mIndex,
747 else if (ch == '"' || ch == '\\')
748 mBuffer[mIndex++] = '\\';
749 mBuffer[mIndex++] = ch;
752 if (mIndex + 1 >= DEBUG_BUFFER_SIZE)
758 void CacheBuilder::Debug::validateFrame() {
759 Frame* frame = frameAnd();
760 Page* page = frame->page();
762 ASSERT((int) page > 0x10000);
763 Frame* child = frame->tree()->firstChild();
765 Builder(child)->mDebug.validateFrame();
766 child = child->tree()->nextSibling();
770 void CacheBuilder::Debug::wideString(const UChar* chars, int length, bool hex) {
775 uChar(chars, length, hex);
780 void CacheBuilder::Debug::wideString(const String& str) {
781 wideString(str.characters(), str.length(), false);
784 #endif // DUMP_NAV_CACHE
786 CacheBuilder::CacheBuilder()
788 mAllowableTypes = ALL_CACHEDNODE_BITS;
789 #ifdef DUMP_NAV_CACHE_USING_PRINTF
790 gNavCacheLogFile = NULL;
794 void CacheBuilder::adjustForColumns(const ClipColumnTracker& track,
795 CachedNode* node, IntRect* bounds, RenderBlock* renderer)
797 if (!renderer->hasColumns())
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);
808 IntRect test = *bounds;
810 if (column.contains(test)) {
817 int xOffset = column.width() + columnGap;
818 x += track.mDirection == LTR ? xOffset : -xOffset;
819 y -= column.height();
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())
832 bool CacheBuilder::AnyChildIsClick(Node* node)
834 AtomicString eventTypes[5] = {
835 eventNames().clickEvent,
836 eventNames().mousedownEvent,
837 eventNames().mouseupEvent,
838 eventNames().keydownEvent,
839 eventNames().keyupEvent
842 Node* child = node->firstChild();
843 while (child != NULL) {
844 if (child->isFocusable() ||
845 NodeHasEventListeners(child, eventTypes, 5))
847 if (AnyChildIsClick(child))
849 child = child->nextSibling();
854 bool CacheBuilder::AnyIsClick(Node* node)
856 if (node->hasTagName(HTMLNames::bodyTag))
857 return AnyChildIsClick(node);
859 AtomicString eventTypeSetOne[4] = {
860 eventNames().mouseoverEvent,
861 eventNames().mouseoutEvent,
862 eventNames().keydownEvent,
863 eventNames().keyupEvent
866 if (!NodeHasEventListeners(node, eventTypeSetOne, 4))
869 AtomicString eventTypeSetTwo[3] = {
870 eventNames().clickEvent,
871 eventNames().mousedownEvent,
872 eventNames().mouseupEvent
875 if (NodeHasEventListeners(node, eventTypeSetTwo, 3))
878 return AnyChildIsClick(node);
881 void CacheBuilder::buildCache(CachedRoot* root)
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);
890 static Node* ParentWithChildren(Node* node)
893 while ((parent = parent->parentNode())) {
894 if (parent->childNodeCount() > 1)
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)
908 Node* sibling = NULL;
909 while ((parent = parent->parentNode()) != NULL) {
910 sibling = parent->nextSibling();
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)
930 #if USE(ACCELERATED_COMPOSITING)
931 static void AddLayer(CachedFrame* frame, size_t index, const IntPoint& location, int id)
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);
943 static int FindColorIndex(WTF::Vector<CachedColor>& colorTracker,
944 const CachedColor& cachedColor)
946 CachedColor* work = colorTracker.begin() - 1;
947 CachedColor* end = colorTracker.end();
948 while (++work < end) {
949 if (*work == cachedColor)
950 return work - colorTracker.begin();
952 int result = colorTracker.size();
953 colorTracker.grow(result + 1);
954 CachedColor& newColor = colorTracker.last();
955 newColor = cachedColor;
959 static void InitColor(CachedColor* color)
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());
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
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
983 // keep nodes that are focusable
984 void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
985 CachedRoot* cachedRoot, CachedFrame* cachedFrame)
987 WTF::Vector<FocusTracker> tracker(1); // sentinel
989 FocusTracker* baseTracker = tracker.data();
990 bzero(baseTracker, sizeof(FocusTracker));
991 baseTracker->mCachedNodeIndex = -1;
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());
1002 char* frameNamePtr = cachedFrame->mDebug.mFrameName;
1003 Builder(frame)->mDebug.frameName(frameNamePtr, frameNamePtr +
1004 sizeof(cachedFrame->mDebug.mFrameName) - 1);
1005 *frameNamePtr = '\0';
1009 Document* doc = frame->document();
1011 CachedNode cachedParentNode;
1012 cachedParentNode.init(parent);
1014 cachedParentNode.mDebug.mNodeIndex = nodeIndex;
1016 cachedFrame->add(colorTracker[0]);
1017 cachedFrame->add(cachedParentNode);
1018 Node* node = parent;
1020 int colorIndex = 0; // assume no special css ring colors
1021 const void* lastStyleDataPtr = 0;
1022 int textInputIndex = 0;
1023 Node* focused = doc->focusedNode();
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);
1037 while (walk.mMore || (node = node->traverseNextNode()) != NULL) {
1041 FocusTracker* last = &tracker.last();
1042 int lastChildIndex = cachedFrame->size() - 1;
1043 while (node == last->mLastChild) {
1044 if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
1046 tracker.removeLast();
1047 lastChildIndex = last->mCachedNodeIndex;
1048 last = &tracker.last();
1051 const ClipColumnTracker* lastClip = &clipTracker.last();
1052 if (node != lastClip->mLastChild)
1054 clipTracker.removeLast();
1057 const LayerTracker* lastLayer = &layerTracker.last();
1058 if (node != lastLayer->mLastChild)
1060 layerTracker.removeLast();
1063 const TabIndexTracker* lastTabIndex = &tabIndexTracker.last();
1064 if (node != lastTabIndex->mLastChild)
1066 tabIndexTracker.removeLast();
1068 Frame* child = HasFrame(node);
1069 CachedNode cachedNode;
1070 if (child != NULL) {
1071 if (child->document() == NULL)
1073 RenderObject* nodeRenderer = node->renderer();
1074 if (nodeRenderer != NULL && nodeRenderer->style()->visibility() == HIDDEN)
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);
1085 cachedNode.mDebug.mNodeIndex = nodeIndex;
1086 cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex(
1087 node, nodeIndex, NULL);
1089 cachedFrame->add(cachedNode);
1090 CachedFrame* childPtr = cachedFrame->lastChild();
1091 BuildFrame(root, child, cachedRoot, childPtr);
1094 int tabIndex = node->tabIndex();
1095 Node* lastChild = node->lastChild();
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);
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)
1112 isTransparent = nodeRenderer->hasBackground() == false;
1113 #ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
1114 hasCursorRing = style->tapHighlightColor().alpha() > 0;
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);
1123 bool more = walk.mMore;
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;
1134 TextDirection direction = LTR;
1136 CachedNodeType type = NORMAL_CACHEDNODETYPE;
1137 CachedColor cachedColor;
1138 CachedInput cachedInput;
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);
1150 isUnclipped = true; // FIXME: areamaps require more effort to detect
1151 // assume areamaps are always visible for now
1155 if (nodeRenderer == NULL)
1158 // some common setup
1159 absBounds = nodeRenderer->absoluteBoundingBoxRect();
1160 originalAbsBounds = absBounds;
1161 absBounds.move(globalOffsetX, globalOffsetY);
1162 hasClip = nodeRenderer->hasOverflowClip();
1164 if (node->hasTagName(HTMLNames::canvasTag))
1165 mPictureSetDisabled = true;
1166 if (checkForPluginViewThatWantsFocus(nodeRenderer)) {
1170 type = PLUGIN_CACHEDNODETYPE;
1173 // Only use the root contentEditable element
1174 if (node->rendererIsEditable() && !node->parentOrHostNode()->rendererIsEditable()) {
1177 type = CONTENT_EDITABLE_CACHEDNODETYPE;
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();
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);
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());
1203 if (node->isTextNode() && mAllowableTypes != NORMAL_CACHEDNODE_BITS) {
1204 if (last->mSomeParentTakesFocus) // don't look at text inside focusable node
1206 CachedNodeType checkType;
1207 if (isFocusableText(&walk, more, node, &checkType,
1208 &exported) == false)
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);
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)
1230 cachedNode.setBounds(bounds);
1231 if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH)
1233 if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT)
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
1241 if (node->hasTagName(WebCore::HTMLNames::inputTag)) {
1242 HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1243 if (input->isTextField()) {
1244 if (input->readOnly())
1246 type = TEXT_INPUT_CACHEDNODETYPE;
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())
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())
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))
1278 if (node->disabled())
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;
1289 if (type == TEXT_INPUT_CACHEDNODETYPE) {
1290 RenderTextControl* renderText =
1291 static_cast<RenderTextControl*>(nodeRenderer);
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();
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);
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());
1318 if (type != ANCHOR_CACHEDNODETYPE) {
1319 bool isFocusable = node->isKeyboardFocusable(NULL) ||
1320 node->isMouseFocusable() || node->isFocusable();
1321 if (isFocusable == false) {
1322 if (node->disabled())
1324 bool overOrOut = HasOverOrOut(node);
1325 bool hasTrigger = HasTriggerEvent(node);
1326 if (overOrOut == false && hasTrigger == false)
1328 takesFocus = hasTrigger;
1331 computeCursorRings = true;
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)
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);
1364 IntRect clip = hasClip ? bounds : absBounds;
1365 size_t clipIndex = clipTracker.size();
1366 if (clipTracker.last().mNode == node)
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));
1374 const IntRect& parentClip = clipTrack.mBounds;
1375 if (hasClip == false && type == ANCHOR_CACHEDNODETYPE)
1378 clip.intersect(parentClip);
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;
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
1396 isUnclipped = true; // assume that layers do not have occluded nodes
1398 AddLayer(cachedFrame, cachedFrame->size(), layerClip.location(),
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
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);
1431 cachedNode.setDataIndex(-1);
1433 cachedNode.mDebug.mNodeIndex = nodeIndex;
1434 cachedNode.mDebug.mParentGroupIndex = Debug::ParentIndex(
1435 node, nodeIndex, (Node*) cachedNode.parentGroup());
1437 cachedFrame->add(cachedNode);
1439 int lastIndex = cachedFrame->size() - 1;
1440 if (node == focused) {
1441 CachedNode* cachedNodePtr = cachedFrame->getIndex(lastIndex);
1442 cachedRoot->setCachedFocus(cachedFrame, cachedNodePtr);
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;
1455 while (tracker.size() > 1) {
1456 FocusTracker* last = &tracker.last();
1457 int lastChildIndex = cachedFrame->size() - 1;
1458 if (CleanUpContainedNodes(cachedRoot, cachedFrame, last, lastChildIndex))
1460 tracker.removeLast();
1464 bool CacheBuilder::CleanUpContainedNodes(CachedRoot* cachedRoot,
1465 CachedFrame* cachedFrame, const FocusTracker* last, int lastChildIndex)
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)
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();
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);
1502 frame->setIndexInParent(index);
1504 cachedFrame->removeLast();
1506 cachedRoot->setCachedFocus(cachedFrame, cachedFrame->lastNode());
1510 Node* CacheBuilder::currentFocus() const
1512 Frame* frame = FrameAnd(this);
1513 Document* doc = frame->document();
1515 Node* focus = doc->focusedNode();
1519 Frame* child = frame->tree()->firstChild();
1521 CacheBuilder* cacheBuilder = Builder(child);
1522 Node* focus = cacheBuilder->currentFocus();
1525 child = child->tree()->nextSibling();
1530 static bool strCharCmp(const char* matches, const UChar* test, int wordLength,
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;
1547 static const int stateTwoLetter[] = {
1548 0x02060c00, // A followed by: [KLRSZ]
1550 0x00084001, // C followed by: [AOT]
1551 0x00000014, // D followed by: [CE]
1553 0x00001800, // F followed by: [LM]
1554 0x00100001, // G followed by: [AU]
1555 0x00000100, // H followed by: [I]
1556 0x00002809, // I followed by: [ADLN]
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]
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]
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
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))
1584 int bitcount (unsigned int n)
1597 static bool isUnicodeSpace(UChar ch)
1599 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == 0xa0;
1602 static bool validZip(int stateIndex, const UChar* zipPtr)
1604 static const struct {
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
1671 int zip = zipPtr[0] - '0';
1673 zip += zipPtr[1] - '0';
1674 int low = zipRange[stateIndex].mLow;
1675 int high = zipRange[stateIndex].mHigh;
1676 if (zip >= low && zip <= high)
1678 if (zip == zipRange[stateIndex].mException1)
1680 if (zip == zipRange[stateIndex].mException2)
1685 #define MAX_PLACE_NAME_LENGTH 25 // the longest allowable one word place name
1687 CacheBuilder::FoundState CacheBuilder::FindAddress(const UChar* chars,
1688 unsigned length, int* start, int* end, bool caseInsensitive)
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);
1700 *start = addressState.mStartResult;
1701 *end = addressState.mEndResult;
1705 CacheBuilder::FoundState CacheBuilder::FindPartialAddress(const UChar* baseChars,
1706 const UChar* chars, unsigned length, FindState* s)
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
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
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
1740 "\x05" "ADiaL" "\x03" "AMP" "\x04" "aNCH" "\x05" "aPiD*"
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"
1752 "\x04" "ALK*" "\x03" "ALL" "\x03" "AY*" "\x04" "eLl*", // W
1753 "\x03" "ING" "\x02" "RD", // X
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",
1760 "\x8b" "alifornia" "\x06" "\x95" "o" "\x87" "lorado" "\x07" "\x8a" "nnecticut" "\x08",
1761 "\x89" "elaware" "\x0a" "\x95" "istrict of Columbia" "\x09",
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"
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",
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"
1791 #if 0 // DEBUG_NAV_UI
1792 static char const* const progressNames[] = {
1796 "NUMBER_TRAILING_SPACE",
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
1817 s->mCurrentStart = chars;
1818 s->mEnd = chars + length;
1821 bool mustBeAllUpper = false;
1822 bool secondHalf = false;
1824 UChar ch = s->mCurrent;
1825 while (++chars <= s->mEnd) {
1827 ch = chars < s->mEnd ? *chars : ' ';
1828 switch (s->mProgress) {
1830 if (WTF::isASCIIDigit(ch) == false) {
1831 if (ch != 'O') // letter 'O', not zero
1833 if (s->mEnd - chars < 3)
1837 if ((prior != 'n' || ch != 'e') && (prior != 'N' || ch != 'E'))
1839 if (isUnicodeSpace(*++chars) == false)
1841 s->mProgress = ADDRESS_LINE;
1842 s->mStartResult = chars - 3 - s->mCurrentStart;
1845 if (isUnicodeSpace(prior) == false) {
1846 s->mProgress = SKIP_TO_SPACE;
1849 s->mNumberCount = 1;
1850 s->mProgress = HOUSE_NUMBER;
1851 s->mStartResult = chars - s->mCurrentStart;
1854 if (isUnicodeSpace(ch) == false)
1858 if (WTF::isASCIIDigit(ch)) {
1859 if (++s->mNumberCount >= 6)
1860 s->mProgress = SKIP_TO_SPACE;
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;
1869 if (s->mNumberCount > 0) { // permit 21-23 ELM ST
1874 s->mNumberCount = 0;
1875 s->mProgress = NUMBER_TRAILING_SPACE;
1876 case NUMBER_TRAILING_SPACE:
1877 if (isUnicodeSpace(ch))
1879 if (0 && WTF::isASCIIDigit(ch)) {
1880 s->mNumberCount = 1;
1881 s->mProgress = HOUSE_NUMBER;
1882 s->mStartResult = chars - s->mCurrentStart;
1885 if (WTF::isASCIIDigit(ch) == false && WTF::isASCIIUpper(ch) == false)
1887 s->mProgress = ADDRESS_LINE;
1889 if (WTF::isASCIIAlpha(ch) || ch == '\'' || ch == '-' || ch == '&' || ch == '(' || ch == ')') {
1890 if (++s->mLetterCount > 1) {
1891 s->mNumberWords &= ~(1 << s->mWordCount);
1894 if (s->mNumberCount >= 5)
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;
1910 } else if (s->mLetterCount >= MAX_PLACE_NAME_LENGTH) {
1912 } else if (s->mFirstLower != NULL) {
1913 if (s->mCaseInsensitive)
1915 size_t length = chars - s->mFirstLower;
1918 if (length == 3 && strCharCmp("and" "the", s->mFirstLower, 3, 2) == false)
1920 if (length == 2 && strCharCmp("at" "by" "el" "in" "of", s->mFirstLower, 2, 5) == false)
1924 if (ch == ',' || ch == '*') { // delimits lines
1925 // asterisk as delimiter: http://www.sa.sc.edu/wellness/members.html
1926 if (++s->mLineCount > 5)
1930 if (isUnicodeSpace(ch) || prior == '-') {
1932 if (s->mUnparsed == false)
1934 const UChar* candidate = s->mWords[s->mWordCount];
1935 UChar firstLetter = candidate[0];
1936 if (WTF::isASCIIUpper(firstLetter) == false && WTF::isASCIIDigit(firstLetter) == false)
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)
1945 while ((s->mNumberWords & (1 << shift)) == 0)
1947 s->mNumberWords >>= ++shift;
1948 if (s->mBases[0] != s->mBases[shift]) // if we're past the original node, bail
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
1955 if (s->mWordCount < 4)
1958 if (firstLetter > 'W' - 'A')
1960 UChar secondLetter = candidate[1];
1962 s->mLetterCount--; // trim trailing dashes, to accept CA-94043
1963 if (s->mLetterCount == 2) {
1964 secondLetter -= 'A';
1965 if (secondLetter > 'Z' - 'A')
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;
1978 s->mStates = longStateNames[firstLetter];
1979 if (s->mStates == NULL)
1981 mustBeAllUpper = false;
1982 s->mProgress = STATE_NAME;
1983 unsigned char section = s->mStates[0];
1984 ASSERT(section > 0x80);
1985 s->mSectionLength = section & 0x7f;
1988 s->mStateWord = s->mWordCount - 1;
1991 if (WTF::isASCIIDigit(ch)) {
1992 if (s->mLetterCount == 0) {
1993 if (++s->mNumberCount > 1)
1995 if (s->mWordCount == 0 && s->mContinuationNode)
1997 s->newWord(baseChars, chars);
1998 s->mNumberWords |= 1 << s->mWordCount;
1999 s->mUnparsed = true;
2003 if (ch == '.') { // optionally can follow letters
2004 if (s->mLetterCount == 0)
2006 if (s->mNumberCount > 0)
2010 if (ch == '/') // between numbers (1/2) between words (12 Main / Ste 4d)
2012 if (ch == '#') // can precede numbers, allow it to appear randomly
2014 if (ch == '"') // sometimes parts of addresses are quoted (FIXME: cite an example here)
2018 if (WTF::isASCIIAlpha(ch)) {
2019 if (s->mLetterCount == 0) {
2020 s->newWord(baseChars, chars);
2026 if (WTF::isASCIIDigit(ch) == false) {
2027 if (s->mLetterCount > 0) {
2028 s->mProgress = STATE_NAME;
2035 s->mProgress = ADDRESS_LINE;
2039 // pick up length of first section
2044 bool subStr = false;
2046 unsigned char match = s->mStates[stateIndex];
2047 if (match >= 0x80) {
2048 if (stateIndex == s->mSectionLength)
2052 // goto foundStateName;
2054 skip = match & 0x7f;
2055 match = s->mStates[++stateIndex];
2057 UChar candChar = s->mWords[s->mWordCount - 1][candIndex];
2058 if (mustBeAllUpper && WTF::isASCIILower(candChar))
2060 if (match != candChar) {
2061 if (match != WTF::toASCIILower(candChar)) {
2063 if (subStr == false)
2065 if (stateIndex == s->mSectionLength) {
2067 s->mProgress = ADDRESS_LINE;
2075 continue; // try next substring
2077 mustBeAllUpper = true;
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;
2086 if (nextindex + 1 == s->mSectionLength || skip == 2) {
2087 s->mZipHint = s->mStates[nextindex] - 1;
2088 goto foundStateName;
2093 s->mStates += s->mSectionLength;
2094 ASSERT(s->mStates[0] == 0 || (unsigned) s->mStates[0] > 0x80);
2095 s->mSectionLength = s->mStates[0] & 0x7f;
2098 } while (s->mSectionLength != 0);
2099 s->mProgress = ADDRESS_LINE;
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;
2114 if (WTF::isASCIIDigit(ch)) {
2115 int count = ++s->mNumberCount;
2117 if (WTF::isASCIIDigit(prior))
2120 s->mZipStart = chars;
2124 } else if (isUnicodeSpace(ch)) {
2125 if (s->mNumberCount == 0) {
2126 s->mZipDelimiter = true; // two spaces delimit state name
2129 } else if (ch == '-') {
2130 if (s->mNumberCount == 5 && validZip(s->mZipHint, s->mZipStart)) {
2131 s->mNumberCount = 0;
2132 s->mProgress = PLUS_4;
2135 if (s->mNumberCount == 0)
2136 s->mZipDelimiter = true;
2137 } else if (WTF::isASCIIAlpha(ch) == false)
2138 s->mZipDelimiter = true;
2140 if (s->mLetterCount == 0) {
2141 s->newWord(baseChars, chars);
2142 s->mUnparsed = true;
2146 if (s->mNumberCount == 5 || s->mNumberCount == 9) {
2147 if (validZip(s->mZipHint, s->mZipStart) == false)
2149 s->mEndResult = chars - s->mCurrentStart;
2150 s->mEndWord = s->mWordCount - 1;
2151 } else if (s->mZipDelimiter == false) {
2154 s->mProgress = ADDRESS_LINE;
2157 s->mProgress = FIND_STREET;
2160 if (WTF::isASCIIDigit(ch)) {
2161 if (++s->mNumberCount <= 4)
2164 if (isUnicodeSpace(ch)) {
2165 if (s->mNumberCount == 0)
2168 if (s->mNumberCount == 4) {
2169 if (WTF::isASCIIAlpha(ch) == false) {
2170 s->mEndResult = chars - s->mCurrentStart;
2171 s->mEndWord = s->mWordCount - 1;
2173 } else if (s->mNumberCount != 0)
2175 s->mProgress = FIND_STREET;
2179 for (int wordsIndex = s->mStateWord - 1; wordsIndex >= 0; --wordsIndex) {
2180 const UChar* test = s->mWords[wordsIndex];
2181 UChar letter = test[0];
2183 if (letter > 'X' - 'A')
2185 const char* names = longStreetNames[letter];
2189 while ((offset = *names++) != 0) {
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')
2200 bool fullOnly = WTF::isASCIILower(nameLetter);
2201 nameLetter = WTF::toASCIIUpper(nameLetter);
2202 if (testUpper == nameLetter) {
2203 if (abbr && fullOnly)
2208 if (fullOnly == false)
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) {
2217 int wordReduction = -1;
2219 while ((s->mNumberWords & (1 << shift)) == 0)
2221 if (shift > wordsIndex)
2223 wordReduction = shift;
2224 } while (s->mNumberWords >> ++shift != 0);
2225 if (wordReduction >= 0) {
2226 if (s->mContinuationNode) {
2231 s->mStartResult = s->mWords[wordReduction] - s->mStarts[wordReduction];
2234 if (wordsIndex != s->mStateWord - 1)
2235 return FOUND_COMPLETE;
2243 s->mProgress = ADDRESS_LINE;
2247 if (s->mNumberWords != 0) {
2249 while ((s->mNumberWords & (1 << shift)) == 0)
2251 s->mNumberWords >>= ++shift;
2252 if (s->mBases[0] != s->mBases[shift])
2254 s->shiftWords(shift);
2255 s->mStartResult = s->mWords[0] - s->mStarts[0];
2256 s->mWordCount -= shift;
2257 s->mProgress = ADDRESS_LINE;
2263 if (s->mContinuationNode)
2265 s->mProgress = NO_ADDRESS;
2266 s->mWordCount = s->mLineCount = 0;
2267 s->mNumberWords = 0;
2271 s->mNumberCount = s->mLetterCount = 0;
2272 s->mFirstLower = NULL;
2273 s->mUnparsed = false;
2276 return s->mProgress == NO_ADDRESS ? FOUND_NONE : FOUND_PARTIAL;
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
2285 CacheBuilder::FoundState CacheBuilder::FindPartialEMail(const UChar* chars, unsigned length,
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]
2315 0x00180010, // y followed by: [etu]
2316 0x00401001, // z followed by: [amw]
2319 static char const* const longDomainNames[] = {
2320 "\x03" "ero" "\x03" "rpa", // aero, arpa
2322 "\x02" "at" "\x02" "om" "\x03" "oop", // cat, com, coop
2328 "\x03" "nfo" "\x02" "nt", // info, int
2329 "\x03" "obs", // jobs
2332 "\x02" "il" "\x03" "obi" "\x05" "useum", // mil, mobi, museum
2333 "\x03" "ame" "\x02" "et", // name, net
2334 "\x02" "rg", // , org
2339 "\x05" "ravel", // travel
2348 const UChar* start = chars;
2349 const UChar* end = chars + length;
2350 while (chars < end) {
2351 UChar ch = *chars++;
2354 const UChar* atLocation = chars - 1;
2355 // search for domain
2356 ch = *chars++ | 0x20; // convert uppercase to lower
2357 if (ch < 'a' || ch > 'z')
2359 while (chars < end) {
2361 if (IsDomainChar(ch) == false)
2365 UChar firstLetter = *chars++ | 0x20; // first letter of the domain
2367 return FOUND_NONE; // only one letter; must be at least two
2369 if (firstLetter > 'z' - 'a')
2370 continue; // non-letter followed '.'
2371 int secondLetterMask = domainTwoLetter[firstLetter];
2372 ch = *chars | 0x20; // second letter of the domain
2374 if (ch >= 'z' - 'a')
2376 bool secondMatch = (secondLetterMask & 1 << ch) != 0;
2377 const char* wordMatch = longDomainNames[firstLetter];
2379 while (wordMatch != NULL) {
2380 int len = *wordMatch++;
2383 match = wordMatch[wordIndex];
2385 goto foundDomainStart;
2386 if (chars[wordIndex] != match)
2391 if (*wordMatch == '\0')
2402 if (IsDomainChar(ch))
2404 } else if (chars + 1 < end && IsDomainChar(chars[1]))
2407 // found domain. Search backwards from '@' for beginning of email address
2408 s->mEndResult = chars - start;
2414 goto nextAt; // mailbox can't end in period
2416 if (IsMailboxChar(ch) == false) {
2424 UChar firstChar = *chars;
2425 if (firstChar == '.' || firstChar == '@') // mailbox can't start with period or be empty
2427 s->mStartResult = chars - start;
2428 return FOUND_COMPLETE;
2434 chars = atLocation + 1;
2439 #define PHONE_PATTERN "(200) /-.\\ 100 -. 0000" // poor man's regex: parens optional, any one of punct, digit smallest allowed
2441 CacheBuilder::FoundState CacheBuilder::FindPartialNumber(const UChar* chars, unsigned length,
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;
2450 bool initialized = s->mInitialized;
2451 while (chars < end) {
2452 if (initialized == false) {
2453 s->mBackTwo = s->mBackOne;
2454 s->mBackOne = s->mCurrent;
2456 UChar ch = s->mCurrent = *chars;
2458 char patternChar = *pattern;
2459 switch (patternChar) {
2461 if (initialized == false) {
2462 s->mStartResult = chars - start;
2467 if (ch < patternChar || ch > '9')
2474 if (WTF::isASCIIDigit(ch) == false) {
2480 if (ch == patternChar)
2484 if (ch == patternChar) {
2485 s->mStartResult = chars - start;
2487 s->mOpenParen = true;
2489 goto commonPunctuation;
2491 if ((ch == patternChar) ^ s->mOpenParen)
2495 if (ch == patternChar) {
2500 } while (++pattern); // never false
2506 if (s->mContinuationNode)
2509 pattern = s->mPattern;
2510 store = s->mStorePtr;
2511 } while (++chars < end);
2513 if (WTF::isASCIIDigit(s->mBackOne != '1' ? s->mBackOne : s->mBackTwo))
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;
2524 CacheBuilder::FoundState CacheBuilder::FindPhoneNumber(const UChar* chars, unsigned length,
2525 int* start, int* end)
2529 FoundState result = FindPartialNumber(chars, length, &state);
2530 *start = state.mStartResult;
2531 *end = state.mEndResult;
2535 void CacheBuilder::FindReset(FindState* state)
2537 memset(state, 0, sizeof(FindState));
2538 state->mCurrent = ' ';
2539 FindResetNumber(state);
2542 void CacheBuilder::FindResetNumber(FindState* state)
2544 state->mOpenParen = false;
2545 state->mPattern = (char*) PHONE_PATTERN;
2546 state->mStorePtr = state->mStore;
2549 IntRect CacheBuilder::getAreaRect(const HTMLAreaElement* area)
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();
2559 for (n = map->firstChild(); n;
2560 n = n->traverseNextNode(map)) {
2562 if (area->isDefault())
2563 return image->absoluteBoundingBoxRect();
2564 return area->computeRect(image);
2573 void CacheBuilder::GetGlobalOffset(Node* node, int* x, int * y)
2575 GetGlobalOffset(node->document()->frame(), x, y);
2578 void CacheBuilder::GetGlobalOffset(Frame* frame, int* x, int* y)
2580 // TIMER_PROBE(__FUNCTION__);
2588 while ((parent = frame->tree()->parent()) != NULL) {
2589 const WebCore::IntRect& rect = frame->view()->platformWidget()->getBounds();
2594 // TIMER_PROBE_END();
2597 Frame* CacheBuilder::HasFrame(Node* node)
2599 RenderObject* renderer = node->renderer();
2600 if (renderer == NULL)
2602 if (renderer->isWidget() == false)
2604 Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
2607 if (widget->isFrameView() == false)
2609 return static_cast<FrameView*>(widget)->frame();
2612 bool CacheBuilder::HasOverOrOut(Node* node)
2614 // eventNames are thread-local data, I avoid using 'static' variable here.
2615 AtomicString eventTypes[2] = {
2616 eventNames().mouseoverEvent,
2617 eventNames().mouseoutEvent
2620 return NodeHasEventListeners(node, eventTypes, 2);
2623 bool CacheBuilder::HasTriggerEvent(Node* node)
2625 AtomicString eventTypes[5] = {
2626 eventNames().clickEvent,
2627 eventNames().mousedownEvent,
2628 eventNames().mouseupEvent,
2629 eventNames().keydownEvent,
2630 eventNames().keyupEvent
2633 return NodeHasEventListeners(node, eventTypes, 5);
2636 // #define EMAIL_PATTERN "x@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
2639 bool CacheBuilder::IsDomainChar(UChar ch)
2641 static const unsigned body[] = {0x03ff6000, 0x07fffffe, 0x07fffffe}; // 0-9 . - A-Z a-z
2643 if (ch > 'z' - 0x20)
2645 return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0;
2648 bool CacheBuilder::isFocusableText(NodeWalk* walk, bool more, Node* node,
2649 CachedNodeType* type, String* exported) const
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();
2657 while (index < length && isUnicodeSpace(baseChars[index]))
2659 if (index >= length)
2661 if (more == false) {
2664 walk->mFinalNode = node;
2665 walk->mLastInline = NULL;
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)
2676 int start = walk->mEnd;
2677 InlineTextBox* saveInline;
2678 int baseStart, firstStart = start;
2679 saveInline = baseInline;
2681 for (CachedNodeType checkType = ADDRESS_CACHEDNODETYPE;
2682 checkType <= PHONE_CACHEDNODETYPE;
2683 checkType = static_cast<CachedNodeType>(checkType + 1))
2685 if ((1 << (checkType - 1) & mAllowableTypes) == 0)
2687 InlineTextBox* inlineTextBox = baseInline;
2688 FindState findState;
2689 FindReset(&findState);
2691 if (checkType == ADDRESS_CACHEDNODETYPE) {
2692 findState.mBases[0] = baseChars;
2693 findState.mWords[0] = baseChars + start;
2694 findState.mStarts[0] = baseChars + start;
2696 Node* lastPartialNode = NULL;
2697 int lastPartialEnd = -1;
2698 bool lastPartialMore = false;
2699 bool firstPartial = true;
2700 InlineTextBox* lastPartialInline = NULL;
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);
2711 case EMAIL_CACHEDNODETYPE:
2712 state = FindPartialEMail(chars, length, &findState);
2714 case PHONE_CACHEDNODETYPE:
2715 state = FindPartialNumber(chars, length, &findState);
2720 findState.mInitialized = state != FOUND_NONE;
2721 if (wasInitialized != findState.mInitialized)
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;
2741 walk->mFinalNode = lastPartialNode;
2742 walk->mEnd = lastPartialEnd;
2743 walk->mMore = lastPartialMore;
2744 walk->mLastInline = lastPartialInline;
2747 if (checkType == PHONE_CACHEDNODETYPE) {
2748 const UChar* store = findState.mStore;
2749 *exported = String(store);
2751 Node* temp = textNode;
2753 start = walk->mStart;
2754 exported->truncate(0);
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)
2768 temp = temp->traverseNextNode();
2770 } while (temp->isTextNode() == false);
2771 // add a space in between text nodes to avoid
2772 // words collapsing together
2773 exported->append(" ");
2778 goto tryNextCheckType;
2779 } else if (findState.mContinuationNode)
2781 if (inlineTextBox == NULL)
2783 inlineTextBox = inlineTextBox->nextTextBox();
2784 if (inlineTextBox == NULL)
2786 start = inlineTextBox->start();
2787 if (state == FOUND_PARTIAL && node == textNode)
2788 findState.mContinuationNode = false;
2790 if (state == FOUND_NONE)
2792 // search for next text node, if any
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) {
2807 inlineTextBox = NULL;
2809 findState.mProgress = FIND_STREET;
2812 goto tryNextCheckType;
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();
2825 findState.mEndResult = 0;
2829 baseInline = saveInline;
2830 string = textNode->dataImpl();
2831 baseChars = string->characters();
2834 CachedNodeType temp = *type;
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);
2844 case EMAIL_CACHEDNODETYPE: {
2845 String encoded = WebCore::encodeWithURLEscapeSequences(*exported);
2846 exported->swap(encoded);
2847 exported->insert(WTF::String("mailto:"), 0);
2849 case PHONE_CACHEDNODETYPE:
2850 exported->insert(WTF::String("tel:"), 0);
2862 bool CacheBuilder::IsMailboxChar(UChar ch)
2864 // According to http://en.wikipedia.org/wiki/Email_address
2865 // ! # $ % & ' * + - . / 0-9 = ?
2868 static const unsigned body[] = {0xa3ffecfa, 0xc7fffffe, 0x7fffffff};
2870 if (ch > '~' - 0x20)
2872 return (body[ch >> 5] & 1 << (ch & 0x1f)) != 0;
2875 bool CacheBuilder::setData(CachedFrame* cachedFrame)
2877 Frame* frame = FrameAnd(this);
2878 Document* doc = frame->document();
2881 RenderObject* renderer = doc->renderer();
2882 if (renderer == NULL)
2884 RenderLayer* layer = renderer->enclosingLayer();
2887 if (layer->width() == 0 || layer->height() == 0)
2892 GetGlobalOffset(frame, &x, &y);
2893 WebCore::IntRect viewBounds = frame->view()->platformWidget()->getBounds();
2895 viewBounds.setLocation(WebCore::IntPoint(x, y));
2896 cachedFrame->setLocalViewBounds(viewBounds);
2897 cachedFrame->setContentsSize(layer->scrollWidth(), layer->scrollHeight());
2898 if (cachedFrame->childCount() == 0)
2900 CachedFrame* lastCachedFrame = cachedFrame->lastChild();
2901 cachedFrame = cachedFrame->firstChild();
2903 CacheBuilder* cacheBuilder = Builder((Frame* )cachedFrame->framePointer());
2904 cacheBuilder->setData(cachedFrame);
2905 } while (cachedFrame++ != lastCachedFrame);
2909 #if USE(ACCELERATED_COMPOSITING)
2910 void CacheBuilder::TrackLayer(WTF::Vector<LayerTracker>& layerTracker,
2911 RenderObject* nodeRenderer, Node* lastChild, int offsetX, int offsetY)
2913 RenderLayer* layer = nodeRenderer->enclosingLayer();
2914 RenderLayerBacking* back = layer->backing();
2917 GraphicsLayer* grLayer = back->graphicsLayer();
2918 if (back->hasContentsLayer())
2919 grLayer = back->foregroundLayer();
2922 LayerAndroid* aLayer = grLayer->platformLayer();
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);
2932 // Prevent a crash when scrolling a layer that does not have a parent.
2933 if (layer->stackingContext())
2934 layer->scrollToOffset(0, 0);
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
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());
2955 bool CacheBuilder::validNode(Frame* startFrame, void* matchFrame,
2958 if (matchFrame == startFrame) {
2959 if (matchNode == NULL)
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())
2972 node = node->traverseNextNode();
2974 DBG_NAV_LOGD("frame=%p valid node=%p invalid\n", matchFrame, matchNode);
2977 Frame* child = startFrame->tree()->firstChild();
2979 bool result = validNode(child, matchFrame, matchNode);
2982 child = child->tree()->nextSibling();
2985 if (startFrame->tree()->parent() == NULL)
2986 DBG_NAV_LOGD("frame=%p node=%p false\n", matchFrame, matchNode);
2991 static int Area(const IntRect& rect)
2993 return rect.width() * rect.height();
2996 bool CacheBuilder::AddPartRect(IntRect& bounds, int x, int y,
2997 WTF::Vector<IntRect>* result, IntRect* focusBounds)
2999 if (bounds.isEmpty())
3002 if (bounds.maxX() <= 0 || bounds.maxY() <= 0)
3004 IntRect* work = result->begin() - 1;
3005 IntRect* end = result->end();
3006 while (++work < end) {
3007 if (work->contains(bounds))
3009 if (bounds.contains(*work)) {
3011 focusBounds->unite(bounds);
3014 if ((bounds.x() != work->x() || bounds.width() != work->width()) &&
3015 (bounds.y() != work->y() || bounds.height() != work->height()))
3017 IntRect test = *work;
3019 if (Area(test) > Area(*work) + Area(bounds))
3022 focusBounds->unite(bounds);
3025 if (result->size() >= MAXIMUM_FOCUS_RING_COUNT)
3027 result->append(bounds);
3028 if (focusBounds->isEmpty())
3029 *focusBounds = bounds;
3031 focusBounds->unite(bounds);
3035 bool CacheBuilder::ConstructPartRects(Node* node, const IntRect& bounds,
3036 IntRect* focusBounds, int x, int y, WTF::Vector<IntRect>* result,
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();
3048 while ((prior = prior->lastChild()) != NULL)
3050 ASSERT(last != NULL);
3051 bool nodeIsAnchor = node->hasTagName(HTMLNames::aTag);
3054 const ClipColumnTracker* lastClip = &clipTracker.last();
3055 if (test != lastClip->mLastChild)
3057 clipTracker.removeLast();
3059 RenderObject* renderer = test->renderer();
3060 if (renderer == NULL)
3062 EVisibility vis = renderer->style()->visibility();
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
3072 if (test->isTextNode()) {
3073 RenderText* renderText = (RenderText*) renderer;
3074 InlineTextBox *textBox = renderText->firstTextBox();
3075 if (textBox == NULL)
3077 if (ConstructTextRect((Text*) test, textBox, 0, INT_MAX,
3078 x, y, focusBounds, clipBounds, result) == false) {
3083 if (test->hasTagName(HTMLNames::imgTag)) {
3084 IntRect bounds = test->getRect();
3085 bounds.intersect(clipBounds);
3086 if (AddPartRect(bounds, x, y, result, focusBounds) == false)
3088 *imageCountPtr += 1;
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)
3101 bounds = IntRect(left, top, right - left, bottom - top);
3102 if (AddPartRect(bounds, x, y, result, focusBounds) == false)
3107 Node* lastChild = test->lastChild();
3108 if (lastChild == NULL)
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);
3115 } while (test != last && (test = test->traverseNextNode()) != NULL);
3117 if (result->size() == 0 || focusBounds->width() < MINIMUM_FOCUSABLE_WIDTH
3118 || focusBounds->height() < MINIMUM_FOCUSABLE_HEIGHT) {
3119 if (bounds.width() < MINIMUM_FOCUSABLE_WIDTH)
3121 if (bounds.height() < MINIMUM_FOCUSABLE_HEIGHT)
3123 result->append(bounds);
3124 *focusBounds = bounds;
3129 static inline bool isNotSpace(UChar c)
3131 return c <= 0xA0 ? isUnicodeSpace(c) == false :
3132 WTF::Unicode::direction(c) != WTF::Unicode::WhiteSpaceNeutral;
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)
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();
3145 int textBoxStart = textBox->start();
3146 int textBoxEnd = textBoxStart + textBox->len();
3147 if (textBoxEnd <= start)
3149 if (textBoxEnd > relEnd)
3150 textBoxEnd = relEnd;
3151 IntRect bounds = textBox->selectionRect((int) pt.x(), (int) pt.y(),
3153 bounds.intersect(clipBounds);
3154 if (bounds.isEmpty())
3156 bool drawable = false;
3157 for (int index = start; index < textBoxEnd; index++)
3158 if ((drawable |= isNotSpace(chars[index])) != false)
3160 if (drawable && vis != HIDDEN) {
3161 if (AddPartRect(bounds, x, y, result, focusBounds) == false)
3164 if (textBoxEnd == relEnd)
3166 } while ((textBox = textBox->nextTextBox()) != NULL);
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)
3175 *focusBounds = IntRect(0, 0, 0, 0);
3177 RenderText* renderText = (RenderText*) node->renderer();
3178 int relEnd = node == last ? end : renderText->textLength();
3179 InlineTextBox *textBox = renderText->firstTextBox();
3180 if (textBox != NULL) {
3182 if ((int) textBox->end() >= start)
3184 } while ((textBox = textBox->nextTextBox()) != NULL);
3185 if (textBox && ConstructTextRect(node, textBox, start, relEnd,
3186 x, y, focusBounds, clipBounds, result) == false)
3193 node = (Text*) node->traverseNextNode();
3194 ASSERT(node != NULL);
3195 } while (node->isTextNode() == false || node->renderer() == NULL);