Changeset 18758 in webkit


Ignore:
Timestamp:
Jan 10, 2007 11:26:44 PM (17 years ago)
Author:
hyatt
Message:

Rework positionForCoordinates to be more efficient and to handle multi-column content. This entailed
adding a new field to HitTestResult called localPoint that cached the mouse coordinate in the coordinate
space of the renderer.

positionForCoordinates now takes local coordinates and so no longer needs to waste time recomputing
absolute positions over and over again.

Reviewed by darin

  • bridge/mac/WebCoreAXObject.mm: (-[WebCoreAXObject value]): (-[WebCoreAXObject doAXTextMarkerRangeForLine:]): (-[WebCoreAXObject doAXTextMarkerForPosition:]):
  • dom/Document.cpp: (WebCore::Document::prepareMouseEvent):
  • editing/SelectionController.cpp: (WebCore::SelectionController::contains):
  • editing/visible_units.cpp: (WebCore::previousLinePosition): (WebCore::nextLinePosition):
  • page/EventHandler.cpp: (WebCore::EventHandler::selectClosestWordFromMouseEvent): (WebCore::EventHandler::handleMousePressEventDoubleClick): (WebCore::EventHandler::handleMousePressEventTripleClick): (WebCore::EventHandler::handleMousePressEventSingleClick): (WebCore::EventHandler::handleMouseMoveEvent): (WebCore::EventHandler::handleMouseReleaseEvent): (WebCore::EventHandler::sendContextMenuEvent):
  • page/EventHandler.h:
  • page/MouseEventWithHitTestResults.cpp: (WebCore::MouseEventWithHitTestResults::MouseEventWithHitTestResults):
  • page/MouseEventWithHitTestResults.h: (WebCore::MouseEventWithHitTestResults::localPoint):
  • page/mac/WebCoreFrameBridge.mm: (-[WebCoreFrameBridge _visiblePositionForPoint:]):
  • rendering/EllipsisBox.cpp: (WebCore::EllipsisBox::nodeAtPoint):
  • rendering/HitTestResult.h: (WebCore::HitTestResult::localPoint): (WebCore::HitTestResult::setLocalPoint):
  • rendering/InlineFlowBox.cpp: (WebCore::InlineFlowBox::nodeAtPoint):
  • rendering/InlineTextBox.cpp: (WebCore::InlineTextBox::nodeAtPoint):
  • rendering/RenderBlock.cpp: (WebCore::RenderBlock::nodeAtPoint): (WebCore::RenderBlock::hitTestContents): (WebCore::RenderBlock::positionForCoordinates):
  • rendering/RenderBox.cpp: (WebCore::RenderBox::nodeAtPoint):
  • rendering/RenderContainer.cpp: (WebCore::RenderContainer::positionForCoordinates):
  • rendering/RenderFlow.cpp: (WebCore::RenderFlow::hitTestLines):
  • rendering/RenderInline.cpp: (WebCore::RenderInline::positionForCoordinates):
  • rendering/RenderLayer.cpp: (WebCore::RenderLayer::autoscroll): (WebCore::RenderLayer::hitTestLayer):
  • rendering/RenderObject.cpp: (WebCore::RenderObject::updateHitTestResult):
  • rendering/RenderObject.h:
  • rendering/RenderPath.cpp: (WebCore::RenderPath::nodeAtPoint):
  • rendering/RenderReplaced.cpp: (WebCore::RenderReplaced::positionForCoordinates):
  • rendering/RenderTableRow.cpp: (WebCore::RenderTableRow::nodeAtPoint):
  • rendering/RenderTableSection.cpp: (WebCore::RenderTableSection::nodeAtPoint):
  • rendering/RenderText.cpp: (WebCore::RenderText::positionForCoordinates):
  • rendering/RootInlineBox.cpp: (WebCore::RootInlineBox::nodeAtPoint): (WebCore::RootInlineBox::closestLeafChildForXPos):
  • rendering/RootInlineBox.h:
Location:
trunk/WebCore
Files:
30 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r18757 r18758  
     12007-01-10  David Hyatt  <hyatt@apple.com>
     2
     3        Rework positionForCoordinates to be more efficient and to handle multi-column content.  This entailed
     4        adding a new field to HitTestResult called localPoint that cached the mouse coordinate in the coordinate
     5        space of the renderer.
     6
     7        positionForCoordinates now takes local coordinates and so no longer needs to waste time recomputing
     8        absolute positions over and over again.
     9
     10        Reviewed by darin
     11
     12        * bridge/mac/WebCoreAXObject.mm:
     13        (-[WebCoreAXObject value]):
     14        (-[WebCoreAXObject doAXTextMarkerRangeForLine:]):
     15        (-[WebCoreAXObject doAXTextMarkerForPosition:]):
     16        * dom/Document.cpp:
     17        (WebCore::Document::prepareMouseEvent):
     18        * editing/SelectionController.cpp:
     19        (WebCore::SelectionController::contains):
     20        * editing/visible_units.cpp:
     21        (WebCore::previousLinePosition):
     22        (WebCore::nextLinePosition):
     23        * page/EventHandler.cpp:
     24        (WebCore::EventHandler::selectClosestWordFromMouseEvent):
     25        (WebCore::EventHandler::handleMousePressEventDoubleClick):
     26        (WebCore::EventHandler::handleMousePressEventTripleClick):
     27        (WebCore::EventHandler::handleMousePressEventSingleClick):
     28        (WebCore::EventHandler::handleMouseMoveEvent):
     29        (WebCore::EventHandler::handleMouseReleaseEvent):
     30        (WebCore::EventHandler::sendContextMenuEvent):
     31        * page/EventHandler.h:
     32        * page/MouseEventWithHitTestResults.cpp:
     33        (WebCore::MouseEventWithHitTestResults::MouseEventWithHitTestResults):
     34        * page/MouseEventWithHitTestResults.h:
     35        (WebCore::MouseEventWithHitTestResults::localPoint):
     36        * page/mac/WebCoreFrameBridge.mm:
     37        (-[WebCoreFrameBridge _visiblePositionForPoint:]):
     38        * rendering/EllipsisBox.cpp:
     39        (WebCore::EllipsisBox::nodeAtPoint):
     40        * rendering/HitTestResult.h:
     41        (WebCore::HitTestResult::localPoint):
     42        (WebCore::HitTestResult::setLocalPoint):
     43        * rendering/InlineFlowBox.cpp:
     44        (WebCore::InlineFlowBox::nodeAtPoint):
     45        * rendering/InlineTextBox.cpp:
     46        (WebCore::InlineTextBox::nodeAtPoint):
     47        * rendering/RenderBlock.cpp:
     48        (WebCore::RenderBlock::nodeAtPoint):
     49        (WebCore::RenderBlock::hitTestContents):
     50        (WebCore::RenderBlock::positionForCoordinates):
     51        * rendering/RenderBox.cpp:
     52        (WebCore::RenderBox::nodeAtPoint):
     53        * rendering/RenderContainer.cpp:
     54        (WebCore::RenderContainer::positionForCoordinates):
     55        * rendering/RenderFlow.cpp:
     56        (WebCore::RenderFlow::hitTestLines):
     57        * rendering/RenderInline.cpp:
     58        (WebCore::RenderInline::positionForCoordinates):
     59        * rendering/RenderLayer.cpp:
     60        (WebCore::RenderLayer::autoscroll):
     61        (WebCore::RenderLayer::hitTestLayer):
     62        * rendering/RenderObject.cpp:
     63        (WebCore::RenderObject::updateHitTestResult):
     64        * rendering/RenderObject.h:
     65        * rendering/RenderPath.cpp:
     66        (WebCore::RenderPath::nodeAtPoint):
     67        * rendering/RenderReplaced.cpp:
     68        (WebCore::RenderReplaced::positionForCoordinates):
     69        * rendering/RenderTableRow.cpp:
     70        (WebCore::RenderTableRow::nodeAtPoint):
     71        * rendering/RenderTableSection.cpp:
     72        (WebCore::RenderTableSection::nodeAtPoint):
     73        * rendering/RenderText.cpp:
     74        (WebCore::RenderText::positionForCoordinates):
     75        * rendering/RootInlineBox.cpp:
     76        (WebCore::RootInlineBox::nodeAtPoint):
     77        (WebCore::RootInlineBox::closestLeafChildForXPos):
     78        * rendering/RootInlineBox.h:
     79
    1802007-01-11  George Staikos  <staikos@kde.org>
    281
  • trunk/WebCore/bridge/mac/WebCoreAXObject.mm

    r18677 r18758  
    580580       
    581581        // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here
    582         VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates (0, 0);
    583         VisiblePosition endVisiblePosition   = m_renderer->positionForCoordinates (LONG_MAX, LONG_MAX);
     582        VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0);
     583        VisiblePosition endVisiblePosition   = m_renderer->positionForCoordinates(LONG_MAX, LONG_MAX);
    584584        if (startVisiblePosition.isNull() || endVisiblePosition.isNull())
    585585            return nil;
     
    12221222    // NOTE: BUG this is wrong when lineNumber is lineCount+1,  because nextLinePosition takes you to the
    12231223    // last offset of the last line
    1224     VisiblePosition visiblePos = [self topRenderer]->positionForCoordinates (0, 0);
     1224    VisiblePosition visiblePos = [self topRenderer]->positionForCoordinates(0, 0);
    12251225    VisiblePosition savedVisiblePos;
    12261226    while (--lineCount != 0) {
     
    12671267    RenderObject* renderer = [self topRenderer];
    12681268    Node* innerNode = NULL;
    1269     NSPoint ourpoint;
    12701269   
    12711270    // locate the node containing the point
     1271    IntPoint pointResult;
    12721272    while (1) {
    12731273        // ask the document layer to hitTest
    12741274        NSPoint windowCoord = [[view window] convertScreenToBase: point];
    1275         ourpoint = [view convertPoint:windowCoord fromView:nil];
    1276        
     1275        IntPoint ourpoint([view convertPoint:windowCoord fromView:nil]);
     1276
    12771277        HitTestRequest request(true, true);
    1278         HitTestResult result = HitTestResult(IntPoint(ourpoint));
     1278        HitTestResult result(ourpoint);
    12791279        renderer->layer()->hitTest(request, result);
    12801280        innerNode = result.innerNode();
    12811281        if (!innerNode || !innerNode->renderer())
    12821282            return nil;
     1283
     1284        pointResult = result.localPoint();
    12831285
    12841286        // done if hit something other than a widget
     
    13031305   
    13041306    // get position within the node
    1305     VisiblePosition pos = innerNode->renderer()->positionForCoordinates ((int)ourpoint.x, (int)ourpoint.y);
     1307    VisiblePosition pos = innerNode->renderer()->positionForPoint(pointResult);
    13061308    return (id) [self textMarkerForVisiblePosition:pos];
    13071309}
  • trunk/WebCore/dom/Document.cpp

    r18712 r18758  
    16641664
    16651665    if (!renderer())
    1666         return MouseEventWithHitTestResults(event, 0, 0, false);
     1666        return MouseEventWithHitTestResults(event, 0, IntPoint(), 0, false);
    16671667
    16681668    HitTestResult result(documentPoint);
     
    16731673
    16741674    bool isOverLink = result.URLElement() && !result.URLElement()->getAttribute(hrefAttr).isNull();
    1675     return MouseEventWithHitTestResults(event, result.innerNode(), result.scrollbar(), isOverLink);
     1675    return MouseEventWithHitTestResults(event, result.innerNode(), result.localPoint(), result.scrollbar(), isOverLink);
    16761676}
    16771677
  • trunk/WebCore/editing/SelectionController.cpp

    r18338 r18758  
    10081008        return false;
    10091009   
    1010     Position pos(innerNode->renderer()->positionForPoint(point).deepEquivalent());
     1010    Position pos(innerNode->renderer()->positionForPoint(result.localPoint()).deepEquivalent());
    10111011    if (pos.isNull())
    10121012        return false;
  • trunk/WebCore/editing/visible_units.cpp

    r18637 r18758  
    444444   
    445445    if (root) {
     446        // FIXME: Can be wrong for multi-column layout.
    446447        int absx, absy;
    447448        containingBlock->absolutePositionForContent(absx, absy);
    448449        if (containingBlock->hasOverflowClip())
    449450            containingBlock->layer()->subtractScrollOffset(absx, absy);
    450         RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
     451        RenderObject *renderer = root->closestLeafChildForXPos(x - absx)->object();
    451452        Node* node = renderer->element();
    452453        if (editingIgnoresContent(node))
    453454            return Position(node->parent(), node->nodeIndex());
    454         return renderer->positionForCoordinates(x, absy + root->topOverflow());
     455        return renderer->positionForCoordinates(x - absx, root->topOverflow());
    455456    }
    456457   
     
    513514   
    514515    if (root) {
     516        // FIXME: Can be wrong for multi-column layout.
    515517        int absx, absy;
    516518        containingBlock->absolutePositionForContent(absx, absy);
    517519        if (containingBlock->hasOverflowClip())
    518520            containingBlock->layer()->subtractScrollOffset(absx, absy);
    519         RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
     521        RenderObject *renderer = root->closestLeafChildForXPos(x - absx)->object();
    520522        Node* node = renderer->element();
    521523        if (editingIgnoresContent(node))
    522524            return Position(node->parent(), node->nodeIndex());
    523         return renderer->positionForCoordinates(x, absy + root->topOverflow());
     525        return renderer->positionForCoordinates(x - absx, root->topOverflow());
    524526    }   
    525527
  • trunk/WebCore/page/EventHandler.cpp

    r18610 r18758  
    126126}
    127127
    128 void EventHandler::selectClosestWordFromMouseEvent(const PlatformMouseEvent& mouse, Node *innerNode)
    129 {
     128void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
     129{
     130    Node* innerNode = result.targetNode();
    130131    Selection newSelection;
    131132
    132133    if (innerNode && innerNode->renderer() && m_mouseDownMayStartSelect && innerNode->renderer()->shouldSelect()) {
    133         IntPoint vPoint = m_frame->view()->windowToContents(mouse.pos());
    134         VisiblePosition pos(innerNode->renderer()->positionForPoint(vPoint));
     134        VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
    135135        if (pos.isNotNull()) {
    136136            newSelection = Selection(pos);
     
    161161        m_beganSelectingText = true;
    162162    else
    163         selectClosestWordFromMouseEvent(event.event(), event.targetNode());
     163        selectClosestWordFromMouseEvent(event);
    164164
    165165    return true;
     
    177177
    178178    Selection newSelection;
    179     IntPoint vPoint = m_frame->view()->windowToContents(event.event().pos());
    180     VisiblePosition pos(innerNode->renderer()->positionForPoint(vPoint));
     179    VisiblePosition pos(innerNode->renderer()->positionForPoint(event.localPoint()));
    181180    if (pos.isNotNull()) {
    182181        newSelection = Selection(pos);
     
    213212        return false;
    214213
    215     VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(vPoint));
     214    VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(event.localPoint()));
    216215    if (visiblePos.isNull())
    217216        visiblePos = VisiblePosition(innerNode, innerNode->caretMinOffset(), DOWNSTREAM);
     
    324323
    325324    // handle making selection
    326     IntPoint vPoint = m_frame->view()->windowToContents(event.event().pos());       
    327     VisiblePosition pos(innerNode->renderer()->positionForPoint(vPoint));
     325    VisiblePosition pos(innerNode->renderer()->positionForPoint(event.localPoint()));
    328326
    329327    updateSelectionForMouseDragOverPosition(pos);
     
    379377        Node *node = event.targetNode();
    380378        if (node && node->isContentEditable() && node->renderer()) {
    381             IntPoint vPoint = m_frame->view()->windowToContents(event.event().pos());
    382             VisiblePosition pos = node->renderer()->positionForPoint(vPoint);
     379            VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint());
    383380            newSelection = Selection(pos);
    384381        }
     
    11291126            || (mev.targetNode() && mev.targetNode()->isContentEditable()))) {
    11301127        m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
    1131         selectClosestWordFromMouseEvent(event, mev.targetNode());
     1128        selectClosestWordFromMouseEvent(mev);
    11321129    }
    11331130   
  • trunk/WebCore/page/EventHandler.h

    r18677 r18758  
    148148
    149149private:
    150     void selectClosestWordFromMouseEvent(const PlatformMouseEvent&, Node* innerNode);
     150    void selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& event);
    151151
    152152    bool handleMouseDoubleClickEvent(const PlatformMouseEvent&);
  • trunk/WebCore/page/MouseEventWithHitTestResults.cpp

    r16815 r18758  
    3636
    3737MouseEventWithHitTestResults::MouseEventWithHitTestResults(const PlatformMouseEvent& event,
    38         PassRefPtr<Node> node, PlatformScrollbar* scrollbar, bool isOverLink)
     38        PassRefPtr<Node> node, const IntPoint& localPoint, PlatformScrollbar* scrollbar, bool isOverLink)
    3939    : m_event(event)
    4040    , m_targetNode(node)
    4141    , m_targetElement(targetElement(m_targetNode.get()))
     42    , m_localPoint(localPoint)
    4243    , m_scrollbar(scrollbar)
    4344    , m_isOverLink(isOverLink)
  • trunk/WebCore/page/MouseEventWithHitTestResults.h

    r16815 r18758  
    2929class PlatformScrollbar;
    3030
     31// FIXME: Why doesn't this class just cache a HitTestResult instead of copying all of HitTestResult's fields over?
    3132class MouseEventWithHitTestResults {
    3233public:
    33     MouseEventWithHitTestResults(const PlatformMouseEvent&, PassRefPtr<Node>, PlatformScrollbar*, bool isOverLink);
     34    MouseEventWithHitTestResults(const PlatformMouseEvent&, PassRefPtr<Node>, const IntPoint& localPoint, PlatformScrollbar*, bool isOverLink);
    3435
    3536    const PlatformMouseEvent& event() const { return m_event; }
    3637    Node* targetNode() const;
     38    const IntPoint& localPoint() const { return m_localPoint; }
    3739    PlatformScrollbar* scrollbar() const { return m_scrollbar; }
    3840    bool isOverLink() const { return m_isOverLink; }
     
    4244    RefPtr<Node> m_targetNode;
    4345    RefPtr<Element> m_targetElement;
     46    IntPoint m_localPoint;
    4447    PlatformScrollbar* m_scrollbar;
    4548    bool m_isOverLink;
  • trunk/WebCore/page/mac/WebCoreFrameBridge.mm

    r18752 r18758  
    12061206{
    12071207    IntPoint outerPoint(point);
    1208     Node* node = m_frame->eventHandler()->hitTestResultAtPoint(outerPoint, true).innerNode();
     1208    HitTestResult result = m_frame->eventHandler()->hitTestResultAtPoint(outerPoint, true);
     1209    Node* node = result.innerNode();
    12091210    if (!node)
    12101211        return VisiblePosition();
     
    12121213    if (!renderer)
    12131214        return VisiblePosition();
    1214     FrameView* outerView = m_frame->view();
    1215     FrameView* innerView = node->document()->view();
    1216     IntPoint innerPoint = innerView->windowToContents(outerView->contentsToWindow(outerPoint));
    1217     VisiblePosition visiblePos = renderer->positionForCoordinates(innerPoint.x(), innerPoint.y());
     1215    VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y());
    12181216    if (visiblePos.isNull())
    12191217        visiblePos = VisiblePosition(Position(node, 0));
  • trunk/WebCore/rendering/EllipsisBox.cpp

    r18290 r18758  
    7272        int mty = ty + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline());
    7373        if (m_markupBox->nodeAtPoint(request, result, x, y, mtx, mty)) {
    74             object()->setInnerNode(result);
     74            object()->updateHitTestResult(result, IntPoint(x - mtx, y - mty));
    7575            return true;
    7676        }
     
    7878
    7979    if (object()->style()->visibility() == VISIBLE && IntRect(tx, ty, m_width, m_height).contains(x, y)) {
    80         object()->setInnerNode(result);
     80        object()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
    8181        return true;
    8282    }
  • trunk/WebCore/rendering/HitTestResult.h

    r17669 r18758  
    4747    Node* innerNonSharedNode() const { return m_innerNonSharedNode.get(); }
    4848    IntPoint point() const { return m_point; }
     49    IntPoint localPoint() const { return m_localPoint; }
    4950    Element* URLElement() const { return m_innerURLElement.get(); }
    5051    PlatformScrollbar* scrollbar() const { return m_scrollbar.get(); }
     
    5354    void setInnerNonSharedNode(Node*);
    5455    void setPoint(const IntPoint& p) { m_point = p; }
     56    void setLocalPoint(const IntPoint& p) { m_localPoint = p; }
    5557    void setURLElement(Element*);
    5658    void setScrollbar(PlatformScrollbar*);
     
    7476    RefPtr<Node> m_innerNonSharedNode;
    7577    IntPoint m_point;
     78    IntPoint m_localPoint; // A point in the local coordinate space of m_innerNonSharedNode's renderer.  Allows us to efficiently
     79                           // determine where inside the renderer we hit on subsequent operations.
    7680    RefPtr<Element> m_innerURLElement;
    7781    RefPtr<PlatformScrollbar> m_scrollbar;
  • trunk/WebCore/rendering/InlineFlowBox.cpp

    r18655 r18758  
    535535    for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) {
    536536        if (!curr->object()->layer() && curr->nodeAtPoint(request, result, x, y, tx, ty)) {
    537             object()->setInnerNode(result);
     537            object()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
    538538            return true;
    539539        }
     
    543543    IntRect rect(tx + m_x, ty + m_y, m_width, m_height);
    544544    if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
    545         object()->setInnerNode(result);
     545        object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space.
    546546        return true;
    547547    }
  • trunk/WebCore/rendering/InlineTextBox.cpp

    r18398 r18758  
    249249    IntRect rect(tx + m_x, ty + m_y, m_width, m_height);
    250250    if (m_truncation != cFullTruncation && object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
    251         object()->setInnerNode(result);
     251        object()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
    252252        return true;
    253253    }
  • trunk/WebCore/rendering/RenderBlock.cpp

    r18734 r18758  
    26282628    if (isPointInScrollbar(result, _x, _y, tx, ty)) {
    26292629        if (hitTestAction == HitTestBlockBackground) {
    2630             setInnerNode(result);
     2630            updateHitTestResult(result, IntPoint(_x - tx, _y - ty));
    26312631            return true;
    26322632        }
     
    26572657        FloatingObject* o;
    26582658        DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects);
    2659         for (it.toLast(); (o = it.current()); --it)
    2660             if (!o->noPaint && !o->node->layer() && o->node->hitTest(request, result, _x, _y,
    2661                                      scrolledX + o->left + o->node->marginLeft() - o->node->xPos(),
    2662                                      scrolledY + o->startY + o->node->marginTop() - o->node->yPos())) {
    2663                 setInnerNode(result);
    2664                 return true;
    2665             }
     2659        for (it.toLast(); (o = it.current()); --it) {
     2660            if (!o->noPaint && !o->node->layer()) {
     2661                int xoffset = scrolledX + o->left + o->node->marginLeft() - o->node->xPos();
     2662                int yoffset =  scrolledY + o->startY + o->node->marginTop() - o->node->yPos();
     2663                if (o->node->hitTest(request, result, _x, _y, xoffset, yoffset)) {
     2664                    updateHitTestResult(result, IntPoint(_x - xoffset, _y - yoffset));
     2665                    return true;
     2666                }
     2667            }
     2668        }
    26662669    }
    26672670
     
    26712674        IntRect boundsRect(tx, ty - topExtra, m_width, m_height + topExtra + borderBottomExtra());
    26722675        if (style()->visibility() == VISIBLE && boundsRect.contains(_x, _y)) {
    2673             setInnerNode(result);
     2676            updateHitTestResult(result, IntPoint(_x - tx, _y - ty + topExtra));
    26742677            return true;
    26752678        }
     
    27172720        // We have to hit-test our line boxes.
    27182721        if (hitTestLines(request, result, x, y, tx, ty, hitTestAction)) {
    2719             setInnerNode(result);
     2722            updateHitTestResult(result, IntPoint(x - tx, y - ty));
    27202723            return true;
    27212724        }
     
    27292732            // table-specific hit-test method (which we should do for performance reasons anyway), then we can remove this check.
    27302733            if (!child->layer() && !child->isFloating() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, childHitTest)) {
    2731                 setInnerNode(result);
     2734                updateHitTestResult(result, IntPoint(x - tx, y - ty));
    27322735                return true;
    27332736            }
     
    27712774        return RenderFlow::positionForCoordinates(x, y);
    27722775
    2773     int absx, absy;
    2774     absolutePositionForContent(absx, absy);
    2775 
    2776     int top = absy + borderTop() + paddingTop();
    2777     int bottom = top + contentHeight();
    2778 
    2779     int left = absx + borderLeft() + paddingLeft();
     2776    int top = borderTop() + paddingTop();
     2777    int bottom = top + contentHeight() + borderTopExtra() + borderBottomExtra();
     2778
     2779    int left = borderLeft() + paddingLeft();
    27802780    int right = left + contentWidth();
    27812781
    27822782    Node* n = element();
    27832783   
     2784    int contentsX = x;
     2785    int contentsY = y - borderTopExtra();
     2786    if (hasOverflowClip())
     2787        m_layer->scrollOffset(contentsX, contentsY);
     2788    if (hasColumns()) {
     2789        IntPoint contentsPoint(contentsX, contentsY);
     2790        adjustPointToColumnContents(contentsPoint);
     2791        contentsX = contentsPoint.x();
     2792        contentsY = contentsPoint.y();
     2793    }
     2794
    27842795    if (isReplaced()) {
    2785         if (y < absy || y < absy + height() && x < absx)
     2796        if (y < 0 || y < height() && x < 0)
    27862797            return VisiblePosition(n, caretMinOffset(), DOWNSTREAM);
    2787         if (y >= absy + height() || y >= absy && x >= absx + width())
     2798        if (y >= height() || y >= 0 && x >= width())
    27882799            return VisiblePosition(n, caretMaxOffset(), DOWNSTREAM);
    27892800    }
     
    27972808        if (y < top || (isEditableRoot && (y < bottom && x < left))) {
    27982809            if (!isEditableRoot)
    2799                 if (RenderObject* c = firstChild()) {
    2800                     VisiblePosition p = c->positionForCoordinates(x, y);
     2810                if (RenderObject* c = firstChild()) { // FIXME: This code doesn't make any sense.  This child could be an inline or a positioned element or a float or a compact, etc.
     2811                    VisiblePosition p = c->positionForCoordinates(contentsX - c->xPos(), contentsY - c->yPos());
    28012812                    if (p.isNotNull())
    28022813                        return p;
     
    28132824        if (y >= bottom || (isEditableRoot && (y >= top && x >= right))) {
    28142825            if (!isEditableRoot)
    2815                 if (RenderObject* c = lastChild()) {
    2816                     VisiblePosition p = c->positionForCoordinates(x, y);
     2826                if (RenderObject* c = lastChild()) { // FIXME: This code doesn't make any sense.  This child could be an inline or a positioned element or a float or a compact, ect.
     2827                    VisiblePosition p = c->positionForCoordinates(contentsX - c->xPos(), contentsY - c->yPos());
    28172828                    if (p.isNotNull())
    28182829                        return p;
     
    28312842        if (!firstRootBox())
    28322843            return VisiblePosition(n, 0, DOWNSTREAM);
    2833            
    2834         int contentsX = absx;
    2835         int contentsY = absy;
    2836         if (hasOverflowClip())
    2837             layer()->subtractScrollOffset(contentsX, contentsY);
    2838 
    2839         if (y < contentsY + firstRootBox()->topOverflow() - verticalLineClickFudgeFactor)
     2844
     2845        if (contentsY < firstRootBox()->topOverflow() - verticalLineClickFudgeFactor)
    28402846            // y coordinate is above first root line box
    28412847            return VisiblePosition(positionForBox(firstRootBox()->firstLeafChild(), true), DOWNSTREAM);
     
    28462852            if (root->nextRootBox())
    28472853                // FIXME: make the break point halfway between the bottom of the previous root box and the top of the next root box
    2848                 bottom = contentsY + root->nextRootBox()->topOverflow();
     2854                bottom = root->nextRootBox()->topOverflow();
    28492855            else
    2850                 bottom = contentsY + root->bottomOverflow() + verticalLineClickFudgeFactor;
     2856                bottom = root->bottomOverflow() + verticalLineClickFudgeFactor;
    28512857            // check if this root line box is located at this y coordinate
    2852             if (y < bottom && root->firstChild()) {
    2853                 InlineBox* closestBox = root->closestLeafChildForXPos(x, contentsX);
     2858            if (contentsY < bottom && root->firstChild()) {
     2859                InlineBox* closestBox = root->closestLeafChildForXPos(x);
    28542860                if (closestBox)
    28552861                    // pass the box a y position that is inside it
    2856                     return closestBox->object()->positionForCoordinates(x, contentsY + closestBox->m_y);
     2862                    return closestBox->object()->positionForCoordinates(contentsX, closestBox->m_y);
    28572863            }
    28582864        }
     
    28692875        if (renderer->height() == 0 || renderer->style()->visibility() != VISIBLE || renderer->isFloatingOrPositioned())
    28702876            continue;
    2871         renderer->absolutePositionForContent(absx, top);
    28722877        RenderObject* next = renderer->nextSibling();
    28732878        while (next && next->isFloatingOrPositioned())
    28742879            next = next->nextSibling();
    28752880        if (next)
    2876             next->absolutePositionForContent(absx, bottom);
     2881            bottom = next->yPos();
    28772882        else
    2878             bottom = top + contentHeight();
    2879         if (y >= top && y < bottom)
    2880             return renderer->positionForCoordinates(x, y);
     2883            bottom = top + scrollHeight();
     2884        if (contentsY >= renderer->yPos() && contentsY < bottom)
     2885            return renderer->positionForCoordinates(contentsX - renderer->xPos(), contentsY - renderer->yPos());
    28812886    }
    28822887   
     
    30163021   
    30173022    ASSERT(m_columnRects && m_columnCount == m_columnRects->size());
     3023}
     3024
     3025void RenderBlock::adjustPointToColumnContents(IntPoint& point) const
     3026{
     3027    // Just bail if we have no columns.
     3028    if (!hasColumns() || !m_columnRects)
     3029        return;
     3030
     3031    // Begin with a result rect that is empty.
     3032    IntRect result;
     3033   
     3034    // Determine which columns we intersect.
     3035    int colGap = columnGap();
     3036    int leftGap = colGap / 2;
     3037    IntPoint columnPoint(m_columnRects->at(0).location());
     3038    for (unsigned i = 0; i < m_columnRects->size(); i++) {
     3039        // Add in half the column gap to the left and right of the rect.
     3040        IntRect colRect = m_columnRects->at(i);
     3041        IntRect gapAndColumnRect(colRect.x() - leftGap, colRect.y(), colRect.width() + colGap, colRect.height());
     3042       
     3043        if (gapAndColumnRect.contains(point))
     3044            // We're inside the column.  Translate the x and y into our column coordinate space.
     3045            point -= columnPoint - colRect.location();
     3046
     3047        // Move to the next position.
     3048        columnPoint.setY(columnPoint.y() + colRect.height());
     3049    }
    30183050}
    30193051
  • trunk/WebCore/rendering/RenderBlock.h

    r18734 r18758  
    283283    virtual bool hasColumns() const { return m_columnCount > 1; }
    284284    void adjustRectForColumns(IntRect&) const;
     285private:
     286    void adjustPointToColumnContents(IntPoint&) const;
    285287
    286288protected:
  • trunk/WebCore/rendering/RenderBox.cpp

    r18734 r18758  
    273273        // then we can remove this check.
    274274        if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) {
    275             setInnerNode(result);
     275            updateHitTestResult(result, IntPoint(x - tx, y - ty));
    276276            return true;
    277277        }
     
    281281    // foreground phase (which is true for replaced elements like images).
    282282    if (action == HitTestForeground && IntRect(tx, ty, m_width, m_height).contains(x, y)) {
    283         setInnerNode(result);
     283        updateHitTestResult(result, IntPoint(x - tx, y - ty));
    284284        return true;
    285285    }
  • trunk/WebCore/rendering/RenderContainer.cpp

    r18724 r18758  
    540540       
    541541    if (isTable() && element()) {
    542         int absx, absy;
    543         absolutePositionForContent(absx, absy);
    544        
    545         int left = absx;
    546         int right = left + contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft();
    547         int top = absy;
    548         int bottom = top + contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom();
    549        
    550         if (x < left || x > right || y < top || y > bottom) {
    551             if (x <= (left + right) / 2)
     542        int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft();
     543        int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom();
     544       
     545        if (x < 0 || x > right || y < 0 || y > bottom) {
     546            if (x <= right / 2)
    552547                return VisiblePosition(Position(element(), 0));
    553548            else
     
    559554    int minDist = INT_MAX;
    560555    RenderObject* closestRenderer = 0;
     556    int newX = x;
     557    int newY = y;
     558    if (isTableRow()) {
     559        newX += xPos();
     560        newY += yPos();
     561    }
    561562    for (RenderObject* renderer = m_firstChild; renderer; renderer = renderer->nextSibling()) {
    562563        if (!renderer->firstChild() && !renderer->isInline() && !renderer->isBlockFlow()
    563564            || renderer->style()->visibility() != VISIBLE)
    564565            continue;
    565 
    566         int absx, absy;
    567         renderer->absolutePositionForContent(absx, absy);
    568        
    569         int top = absy + borderTop() + paddingTop();
     566       
     567        int top = borderTop() + paddingTop() + isTableRow() ? 0 : renderer->xPos();
    570568        int bottom = top + renderer->contentHeight();
    571         int left = absx + borderLeft() + paddingLeft();
     569        int left = borderLeft() + paddingLeft() + isTableRow() ? 0 : renderer->yPos();
    572570        int right = left + renderer->contentWidth();
    573571       
    574         if (x <= right && x >= left && y <= top && y >= bottom)
    575             return renderer->positionForCoordinates(x, y);
    576        
     572        if (x <= right && x >= left && y <= top && y >= bottom) {
     573            if (renderer->isTableRow())
     574                return renderer->positionForCoordinates(x + newX - renderer->xPos(), y + newY - renderer->yPos());
     575            return renderer->positionForCoordinates(x - renderer->xPos(), y - renderer->yPos());
     576        }
     577
    577578        // Find the distance from (x, y) to the box.  Split the space around the box into 8 pieces
    578579        // and use a different compare depending on which piece (x, y) is in.
     
    610611   
    611612    if (closestRenderer)
    612         return closestRenderer->positionForCoordinates(x, y);
     613        return closestRenderer->positionForCoordinates(newX - closestRenderer->xPos(), newY - closestRenderer->yPos());
    613614   
    614615    return VisiblePosition(element(), 0, DOWNSTREAM);
  • trunk/WebCore/rendering/RenderFlow.cpp

    r18734 r18758  
    434434            bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty);
    435435            if (inside) {
    436                 setInnerNode(result);
     436                updateHitTestResult(result, IntPoint(x - tx, y - ty));
    437437                return true;
    438438            }
  • trunk/WebCore/rendering/RenderInline.cpp

    r18559 r18758  
    355355VisiblePosition RenderInline::positionForCoordinates(int x, int y)
    356356{
     357    // Translate the coords from the pre-anonymous block to the post-anonymous block.
     358    RenderBlock* cb = containingBlock();
     359    int parentBlockX = cb->xPos() + x;
     360    int parentBlockY = cb->yPos() + y;
    357361    for (RenderObject* c = continuation(); c; c = c->continuation()) {
     362        RenderObject* contBlock = c;
     363        if (c->isInline())
     364            contBlock = c->containingBlock();
    358365        if (c->isInline() || c->firstChild())
    359             return c->positionForCoordinates(x, y);
     366            return c->positionForCoordinates(parentBlockX - contBlock->xPos(), parentBlockY - contBlock->yPos());
    360367    }
    361368
  • trunk/WebCore/rendering/RenderLayer.cpp

    r18752 r18758  
    872872        HitTestResult result(currentPos);
    873873        if (hitTest(request, result)) {
    874             VisiblePosition pos(result.innerNode()->renderer()->positionForPoint(currentPos));
     874            VisiblePosition pos(result.innerNode()->renderer()->positionForPoint(result.localPoint()));
    875875            currentFrame->eventHandler()->updateSelectionForMouseDragOverPosition(pos);
    876876        }
     
    15901590    // exited the WebView, and so hit testing over a scrollbar hits the content document.
    15911591    if ((request.active || request.mouseUp) && renderer()->isRenderView()) {
    1592         renderer()->setInnerNode(result);
     1592        renderer()->updateHitTestResult(result, result.point());
    15931593        return this;
    15941594    }
  • trunk/WebCore/rendering/RenderObject.cpp

    r18752 r18758  
    26102610}
    26112611
    2612 void RenderObject::setInnerNode(HitTestResult& result)
     2612void RenderObject::updateHitTestResult(HitTestResult& result, const IntPoint& point)
    26132613{
    26142614    if (result.innerNode())
     
    26162616
    26172617    Node* node = element();
     2618    IntPoint localPoint(point);
    26182619    if (isRenderView())
    26192620        node = document()->documentElement();
     
    26252626
    26262627    if (node) {
     2628        if (node->renderer()->continuation() && node->renderer() != this) {
     2629            // We're in the continuation of a split inline.  Adjust our local point to be in the coordinate space
     2630            // of the principal renderer's containing block.  This will end up being the innerNonSharedNode.
     2631            RenderObject* firstBlock = node->renderer()->containingBlock();
     2632           
     2633            // Get our containing block.
     2634            RenderObject* block = this;
     2635            if (isInline())
     2636                block = containingBlock();
     2637       
     2638            localPoint.move(block->xPos() - firstBlock->xPos(), block->yPos() - firstBlock->yPos());
     2639        }
     2640
    26272641        result.setInnerNode(node);
    26282642        if (!result.innerNonSharedNode())
    26292643            result.setInnerNonSharedNode(node);
     2644        result.setLocalPoint(localPoint);
    26302645    }
    26312646}
  • trunk/WebCore/rendering/RenderObject.h

    r18727 r18758  
    527527    bool hitTest(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestFilter = HitTestAll);
    528528    virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
    529     void setInnerNode(HitTestResult&);
     529    void updateHitTestResult(HitTestResult&, const IntPoint&);
    530530
    531531    virtual VisiblePosition positionForCoordinates(int x, int y);
  • trunk/WebCore/rendering/RenderPath.cpp

    r18737 r18758  
    228228        if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(hitPoint, hitRules.requireStroke))
    229229            || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(hitPoint, hitRules.requireFill))) {
    230             setInnerNode(result);
     230            updateHitTestResult(result, IntPoint(_x, _y));
    231231            return true;
    232232        }
  • trunk/WebCore/rendering/RenderReplaced.cpp

    r18637 r18758  
    119119}
    120120
    121 VisiblePosition RenderReplaced::positionForCoordinates(int _x, int _y)
    122 {
    123     InlineBox *box = inlineBoxWrapper();
     121VisiblePosition RenderReplaced::positionForCoordinates(int x, int y)
     122{
     123    InlineBox* box = inlineBoxWrapper();
    124124    if (!box)
    125125        return VisiblePosition(element(), 0, DOWNSTREAM);
    126126
    127     RootInlineBox *root = box->root();
    128 
    129     int absx, absy;
    130     containingBlock()->absolutePosition(absx, absy);
    131 
    132     int top = absy + root->topOverflow();
    133     int bottom = root->nextRootBox() ? absy + root->nextRootBox()->topOverflow() : absy + root->bottomOverflow();
    134 
    135     if (_y < top)
     127    // FIXME: This code is buggy if the replaced element is relative positioned.
     128
     129    RootInlineBox* root = box->root();
     130
     131    int top = root->topOverflow();
     132    int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow();
     133
     134    if (y + yPos() < top)
    136135        return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); // coordinates are above
    137136   
    138     if (_y >= bottom)
     137    if (y + yPos() >= bottom)
    139138        return VisiblePosition(element(), caretMaxOffset(), DOWNSTREAM); // coordinates are below
    140139   
    141140    if (element()) {
    142         if (_x <= absx + xPos() + (width() / 2))
     141        if (x <= width() / 2)
    143142            return VisiblePosition(element(), 0, DOWNSTREAM);
    144 
    145143        return VisiblePosition(element(), 1, DOWNSTREAM);
    146144    }
    147145
    148     return RenderBox::positionForCoordinates(_x, _y);
     146    return RenderBox::positionForCoordinates(x, y);
    149147}
    150148
  • trunk/WebCore/rendering/RenderTableRow.cpp

    r18608 r18758  
    148148        // then we can remove this check.
    149149        if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) {
    150             setInnerNode(result);
     150            updateHitTestResult(result, IntPoint(x - tx, y - ty));
    151151            return true;
    152152        }
  • trunk/WebCore/rendering/RenderTableSection.cpp

    r18634 r18758  
    10031003        // then we can remove this check.
    10041004        if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) {
    1005             setInnerNode(result);
     1005            updateHitTestResult(result, IntPoint(x - tx, y - ty));
    10061006            return true;
    10071007        }
  • trunk/WebCore/rendering/RenderText.cpp

    r18735 r18758  
    209209        return VisiblePosition(element(), 0, DOWNSTREAM);
    210210
    211     int absx, absy;
    212     RenderBlock* cb = containingBlock();
    213     cb->absolutePositionForContent(absx, absy);
    214     if (cb->hasOverflowClip())
    215         cb->layer()->subtractScrollOffset(absx, absy);
    216 
    217211    // Get the offset for the position, since this will take rtl text into account.
    218212    int offset;
    219213
    220214    // FIXME: We should be able to roll these special cases into the general cases in the loop below.
    221     if (firstTextBox() && y < absy + firstTextBox()->root()->bottomOverflow() && x < absx + firstTextBox()->m_x) {
     215    if (firstTextBox() && y <  firstTextBox()->root()->bottomOverflow() && x < firstTextBox()->m_x) {
    222216        // at the y coordinate of the first line or above
    223217        // and the x coordinate is to the left of the first text box left edge
    224         offset = firstTextBox()->offsetForPosition(x - absx);
     218        offset = firstTextBox()->offsetForPosition(x);
    225219        return VisiblePosition(element(), offset + firstTextBox()->m_start, DOWNSTREAM);
    226220    }
    227     if (lastTextBox() && y >= absy + lastTextBox()->root()->topOverflow() && x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
     221    if (lastTextBox() && y >= lastTextBox()->root()->topOverflow() && x >= lastTextBox()->m_x + lastTextBox()->m_width) {
    228222        // at the y coordinate of the last line or below
    229223        // and the x coordinate is to the right of the last text box right edge
    230         offset = lastTextBox()->offsetForPosition(x - absx);
     224        offset = lastTextBox()->offsetForPosition(x);
    231225        return VisiblePosition(element(), offset + lastTextBox()->m_start, DOWNSTREAM);
    232226    }
     
    234228    InlineTextBox* lastBoxAbove = 0;
    235229    for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
    236         if (y >= absy + box->root()->topOverflow()) {
    237             if (y < absy + box->root()->bottomOverflow()) {
    238                 offset = box->offsetForPosition(x - absx);
    239 
    240                 if (x == absx + box->m_x)
     230        if (y >= box->root()->topOverflow()) {
     231            if (y < box->root()->bottomOverflow()) {
     232                offset = box->offsetForPosition(x);
     233
     234                if (x == box->m_x)
    241235                    // the x coordinate is equal to the left edge of this box
    242236                    // the affinity must be downstream so the position doesn't jump back to the previous line
    243237                    return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
    244238
    245                 if (x < absx + box->m_x + box->m_width)
     239                if (x < box->m_x + box->m_width)
    246240                    // and the x coordinate is to the left of the right edge of this box
    247241                    // check to see if position goes in this box
    248242                    return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
    249243
    250                 if (!box->prevOnLine() && x < absx + box->m_x)
     244                if (!box->prevOnLine() && x < box->m_x)
    251245                    // box is first on line
    252246                    // and the x coordinate is to the left of the first text box left edge
  • trunk/WebCore/rendering/RootInlineBox.cpp

    r18263 r18758  
    147147    if (m_ellipsisBox && object()->style()->visibility() == VISIBLE) {
    148148        if (m_ellipsisBox->nodeAtPoint(request, result, x, y, tx, ty)) {
    149             object()->setInnerNode(result);
     149            object()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
    150150            return true;
    151151        }
     
    287287}
    288288
    289 InlineBox* RootInlineBox::closestLeafChildForXPos(int x, int tx)
     289InlineBox* RootInlineBox::closestLeafChildForXPos(int x)
    290290{
    291291    InlineBox* firstLeaf = firstLeafChildAfterBox();
     
    295295
    296296    // Avoid returning a list marker when possible.
    297     if (x <= tx + firstLeaf->m_x && !firstLeaf->object()->isListMarker())
     297    if (x <= firstLeaf->m_x && !firstLeaf->object()->isListMarker())
    298298        // The x coordinate is less or equal to left edge of the firstLeaf.
    299299        // Return it.
    300300        return firstLeaf;
    301301
    302     if (x >= tx + lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker())
     302    if (x >= lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker())
    303303        // The x coordinate is greater or equal to right edge of the lastLeaf.
    304304        // Return it.
     
    307307    for (InlineBox* leaf = firstLeaf; leaf && leaf != lastLeaf; leaf = leaf->nextLeafChild()) {
    308308        if (!leaf->object()->isListMarker()) {
    309             int leafX = tx + leaf->m_x;
     309            int leafX = leaf->m_x;
    310310            if (x < leafX + leaf->m_width)
    311311                // The x coordinate is less than the right edge of the box.
  • trunk/WebCore/rendering/RootInlineBox.h

    r18263 r18758  
    122122    int selectionHeight() { return max(0, selectionBottom() - selectionTop()); }
    123123
    124     InlineBox* closestLeafChildForXPos(int x, int tx);
     124    InlineBox* closestLeafChildForXPos(int x);
    125125
    126126protected:
Note: See TracChangeset for help on using the changeset viewer.