Document* XSLStyleSheet::ownerDocument()
{
    for (XSLStyleSheet* styleSheet = this; styleSheet; styleSheet = styleSheet->parentStyleSheet()) {
        Node* node = styleSheet->ownerNode();
        if (node)
            return &node->document();
    }
    return 0;
}
Example #2
0
void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
{
    Node* node = event->target()->toNode();
    ASSERT(node);
    Frame* frame = node->document()->frame();
    ASSERT(frame);

    const PlatformKeyboardEvent* platformEvent = event->keyEvent();
    if (!platformEvent)
        return;

    if (keyboardEventHadCompositionResults(event))
        return;

    KeyBindingTranslator::EventType type = event->type() == eventNames().keydownEvent ?
        KeyBindingTranslator::KeyDown : KeyBindingTranslator::KeyPress;
    m_keyBindingTranslator.getEditorCommandsForKeyEvent(platformEvent->gdkEventKey(), type, m_pendingEditorCommands);
    if (m_pendingEditorCommands.size() > 0) {

        // During RawKeyDown events if an editor command will insert text, defer
        // the insertion until the keypress event. We want keydown to bubble up
        // through the DOM first.
        if (platformEvent->type() == PlatformEvent::RawKeyDown) {
            if (executePendingEditorCommands(frame, false))
                event->setDefaultHandled();

            return;
        }

        // Only allow text insertion commands if the current node is editable.
        if (executePendingEditorCommands(frame, frame->editor()->canEdit())) {
            event->setDefaultHandled();
            return;
        }
    }

    // Don't allow text insertion for nodes that cannot edit.
    if (!frame->editor()->canEdit())
        return;

    // This is just a normal text insertion, so wait to execute the insertion
    // until a keypress event happens. This will ensure that the insertion will not
    // be reflected in the contents of the field until the keyup DOM event.
    if (event->type() != eventNames().keypressEvent)
        return;

    // Don't insert null or control characters as they can result in unexpected behaviour
    if (event->charCode() < ' ')
        return;

    // Don't insert anything if a modifier is pressed
    if (platformEvent->ctrlKey() || platformEvent->altKey())
        return;

    if (frame->editor()->insertText(platformEvent->text(), event))
        event->setDefaultHandled();
}
void SelectorDataList::executeQueryAll(Node& rootNode, Vector<RefPtr<Node> >& matchedElements) const
{
    if (!canUseFastQuery(rootNode))
        return executeSlowQueryAll(rootNode, matchedElements);

    ASSERT(m_selectors.size() == 1);
    ASSERT(m_selectors[0].selector);

    const CSSSelector* firstSelector = m_selectors[0].selector;

    if (!firstSelector->tagHistory()) {
        // Fast path for querySelectorAll('#id'), querySelectorAl('.foo'), and querySelectorAll('div').
        switch (firstSelector->m_match) {
        case CSSSelector::Id:
            {
                if (rootNode.document().containsMultipleElementsWithId(firstSelector->value()))
                    break;

                // Just the same as getElementById.
                Element* element = rootNode.treeScope().getElementById(firstSelector->value());
                if (element && (isTreeScopeRoot(rootNode) || element->isDescendantOf(&rootNode)))
                    matchedElements.append(element);
                return;
            }
        case CSSSelector::Class:
            return collectElementsByClassName(rootNode, firstSelector->value(), matchedElements);
        case CSSSelector::Tag:
            return collectElementsByTagName(rootNode, firstSelector->tagQName(), matchedElements);
        default:
            break; // If we need another fast path, add here.
        }
    }

    bool matchTraverseRoots;
    OwnPtr<SimpleNodeList> traverseRoots = findTraverseRoots(&rootNode, matchTraverseRoots);
    if (traverseRoots->isEmpty())
        return;

    const SelectorData& selector = m_selectors[0];
    if (matchTraverseRoots) {
        while (!traverseRoots->isEmpty()) {
            Node& node = *traverseRoots->next();
            Element& element = toElement(node);
            if (selectorMatches(selector, element, rootNode))
                matchedElements.append(&element);
        }
        return;
    }

    while (!traverseRoots->isEmpty()) {
        Node* traverseRoot = traverseRoots->next();
        for (Element* element = ElementTraversal::firstWithin(traverseRoot); element; element = ElementTraversal::next(element, traverseRoot)) {
            if (selectorMatches(selector, *element, rootNode))
                matchedElements.append(element);
        }
    }
}
Example #4
0
String createFullMarkup(const Range& range)
{
    Node* node = range.startContainer();
    if (!node)
        return String();

    // FIXME: This is always "for interchange". Is that right?
    return documentTypeString(node->document()) + createMarkup(range, 0, AnnotateForInterchange);
}
Example #5
0
void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
{
    Node* node = event->target()->toNode();
    ASSERT(node);
    Frame* frame = node->document().frame();
    ASSERT(frame);

    const PlatformKeyboardEvent* platformEvent = event->keyEvent();
    if (!platformEvent)
        return;

    // If this was an IME event don't do anything.
    if (platformEvent->windowsVirtualKeyCode() == VK_PROCESSKEY)
        return;

    Vector<WTF::String> pendingEditorCommands;
    getEditorCommandsForKeyEvent(event, pendingEditorCommands);
    if (!pendingEditorCommands.isEmpty()) {

        // During RawKeyDown events if an editor command will insert text, defer
        // the insertion until the keypress event. We want keydown to bubble up
        // through the DOM first.
        if (platformEvent->type() == PlatformEvent::RawKeyDown) {
            if (executePendingEditorCommands(frame, pendingEditorCommands, false))
                event->setDefaultHandled();

            return;
        }

        // Only allow text insertion commands if the current node is editable.
        if (executePendingEditorCommands(frame, pendingEditorCommands, frame->editor().canEdit())) {
            event->setDefaultHandled();
            return;
        }
    }

    // Don't allow text insertion for nodes that cannot edit.
    if (!frame->editor().canEdit())
        return;

    // This is just a normal text insertion, so wait to execute the insertion
    // until a keypress event happens. This will ensure that the insertion will not
    // be reflected in the contents of the field until the keyup DOM event.
    if (event->type() != eventNames().keypressEvent)
        return;

    // Don't insert null or control characters as they can result in unexpected behaviour
    if (event->charCode() < ' ')
        return;

    // Don't insert anything if a modifier is pressed
    if (platformEvent->ctrlKey() || platformEvent->altKey())
        return;

    if (frame->editor().insertText(platformEvent->text(), event))
        event->setDefaultHandled();
}
bool RenderTheme::isFocused(const RenderObject* o) const
{
    Node* node = o->node();
    if (!node)
        return false;
    Document* document = node->document();
    Frame* frame = document->frame();
    return node == document->focusedNode() && frame && frame->selection()->isFocusedAndActive();
}
void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node& child)
{
    if (!child.inDocument()) {
        if (is<ContainerNode>(child))
            notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(child));
        return;
    }
    notifyNodeRemovedFromDocument(insertionPoint, child);
    child.document().notifyRemovePendingSheetIfNeeded();
}
// Rules of self-closure
// 1. No elements in HTML documents use the self-closing syntax.
// 2. Elements w/ children never self-close because they use a separate end tag.
// 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag.
// 4. Other elements self-close.
bool MarkupAccumulator::shouldSelfClose(const Node& node)
{
    if (!inXMLFragmentSerialization() && node.document().isHTMLDocument())
        return false;
    if (node.hasChildNodes())
        return false;
    if (node.isHTMLElement() && !elementCannotHaveEndTag(node))
        return false;
    return true;
}
Example #9
0
void Document::updateRangesAfterNodeMovedToAnotherDocument(const Node& node)
{
    ASSERT(node.document() != this);
    if (m_ranges.isEmpty())
        return;
    AttachedRangeSet ranges = m_ranges;
    AttachedRangeSet::const_iterator end = ranges.end();
    for (AttachedRangeSet::const_iterator it = ranges.begin(); it != end; ++it)
        (*it)->updateOwnerDocumentIfNeeded();
}
void EditorClientBlackBerry::handleKeyboardEvent(KeyboardEvent* event)
{
    Node* node = event->target()->toNode();
    ASSERT(node);
    Frame* frame = node->document()->frame();
    ASSERT(frame);

    ASSERT(m_page->d->m_inputHandler);

    const PlatformKeyboardEvent* platformEvent = event->keyEvent();
    if (!platformEvent)
        return;

    Node* start = frame->selection()->start().node();
    if (!start)
        return;

    if (start->isContentEditable()) {

        // Text insertion commands should only be triggered from keypressEvent.
        // There is an assert guaranteeing this in
        // EventHandler::handleTextInputEvent.  Note that windowsVirtualKeyCode
        // is not set for keypressEvent: special keys should have been already
        // handled in keydownEvent, which is called first.
        if (event->type() == eventNames().keypressEvent) {
            if (event->charCode() == '\r') { // \n has been converted to \r in PlatformKeyboardEventOlympia
                if (frame->editor()->command("InsertNewline").execute(event))
                    event->setDefaultHandled();
            } else if (event->charCode() >= ' ' // Don't insert null or control characters as they can result in unexpected behaviour
                    && !platformEvent->text().isEmpty()) {
                if (frame->editor()->insertText(platformEvent->text(), event))
                    event->setDefaultHandled();
            }
            return;
        }

        if (event->type() != eventNames().keydownEvent)
            return;

        bool handledEvent = false;
        switch (platformEvent->windowsVirtualKeyCode()) {
        case VK_BACK:
            handledEvent = frame->editor()->deleteWithDirection(SelectionController::DirectionBackward, CharacterGranularity, false, true);
            break;
        case VK_DELETE:
            handledEvent = frame->editor()->deleteWithDirection(SelectionController::DirectionForward, CharacterGranularity, false, true);
            break;
        case VK_ESCAPE:
            handledEvent = frame->page()->focusController()->setFocusedNode(0, frame);
            break;
        }
        if (handledEvent)
            event->setDefaultHandled();
    }
}
Example #11
0
Position VisiblePosition::canonicalPosition(const Position& position)
{
    // FIXME (9535):  Canonicalizing to the leftmost candidate means that if we're at a line wrap, we will 
    // ask renderers to paint downstream carets for other renderers.
    // To fix this, we need to either a) add code to all paintCarets to pass the responsibility off to
    // the appropriate renderer for VisiblePosition's like these, or b) canonicalize to the rightmost candidate
    // unless the affinity is upstream.
    Node* node = position.node();
    if (!node)
        return Position();

    node->document()->updateLayoutIgnorePendingStylesheets();

    Position candidate = position.upstream();
    if (candidate.inRenderedContent())
        return candidate;
    candidate = position.downstream();
    if (candidate.inRenderedContent())
        return candidate;

    // When neither upstream or downstream gets us to a candidate (upstream/downstream won't leave 
    // blocks or enter new ones), we search forward and backward until we find one.
    Position next = nextVisiblePosition(position);
    Position prev = previousVisiblePosition(position);
    Node* nextNode = next.node();
    Node* prevNode = prev.node();

    // The new position must be in the same editable element. Enforce that first.
    Node* editingRoot = node->rootEditableElement();
    // If the html element is editable, descending into its body will look like a descent 
    // from non-editable to editable content since rootEditableElement stops at the body 
    // even if the html element is editable.
    if (editingRoot && editingRoot->hasTagName(htmlTag))
        return next.isNotNull() ? next : prev;
        
    bool prevIsInSameEditableElement = prevNode && prevNode->rootEditableElement() == editingRoot;
    bool nextIsInSameEditableElement = nextNode && nextNode->rootEditableElement() == editingRoot;
    if (prevIsInSameEditableElement && !nextIsInSameEditableElement)
        return prev;
        
    if (nextIsInSameEditableElement && !prevIsInSameEditableElement)
        return next;
        
    if (!nextIsInSameEditableElement && !prevIsInSameEditableElement)
        return Position();

    // The new position should be in the same block flow element. Favor that.
    Node *originalBlock = node->enclosingBlockFlowElement();
    bool nextIsOutsideOriginalBlock = !nextNode->isAncestor(originalBlock) && nextNode != originalBlock;
    bool prevIsOutsideOriginalBlock = !prevNode->isAncestor(originalBlock) && prevNode != originalBlock;
    if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock)
        return prev;
        
    return next;
}
Example #12
0
bool LayoutTheme::isFocused(const LayoutObject& o)
{
    Node* node = o.node();
    if (!node)
        return false;

    node = node->focusDelegate();
    Document& document = node->document();
    LocalFrame* frame = document.frame();
    return node == document.focusedElement() && node->focused() && node->shouldHaveFocusAppearance() && frame && frame->selection().isFocusedAndActive();
}
Example #13
0
String createFullMarkup(const Node& node)
{
    // FIXME: This is never "for interchange". Is that right?
    String markupString = createMarkup(node, IncludeNode, 0);

    Node::NodeType nodeType = node.nodeType();
    if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
        markupString = documentTypeString(node.document()) + markupString;

    return markupString;
}
EventDispatcher::EventDispatcher(Node& node, PassRefPtrWillBeRawPtr<Event> event)
    : m_node(node)
    , m_event(event)
#if ENABLE(ASSERT)
    , m_eventDispatched(false)
#endif
{
    ASSERT(m_event.get());
    m_view = node.document().view();
    m_event->initEventPath(*m_node);
}
static void writeDebugInfo(LayoutObject* layoutObject, const AtomicString& output)
{
    Node* node = layoutObject->node();
    if (!node)
        return;
    if (node->isDocumentNode())
        node = toDocument(node)->documentElement();
    if (!node->isElementNode())
        return;
    node->document().postTask(adoptPtr(new WriteDebugInfoTask(toElement(node), output)));
}
Document* XSLStyleSheet::ownerDocument()
{
    for (XSLStyleSheet* styleSheet = this; styleSheet; styleSheet = styleSheet->parentStyleSheet()) {
        if (styleSheet->m_ownerDocument)
            return styleSheet->m_ownerDocument.get();
        Node* node = styleSheet->ownerNode();
        if (node)
            return &node->document();
    }
    return nullptr;
}
Example #17
0
bool RenderTheme::isFocused(const RenderObject* o) const
{
    Node* node = o->node();
    if (!node)
        return false;

    node = node->focusDelegate();
    Document& document = node->document();
    Frame* frame = document.frame();
    return node == document.focusedElement() && node->shouldHaveFocusAppearance() && frame && frame->selection().isFocusedAndActive();
}
Example #18
0
bool RenderTheme::isActive(const RenderObject* o) const
{
    Node* node = o->node();
    if (!node)
        return false;

    Page* page = node->document().page();
    if (!page)
        return false;

    return page->focusController().isActive();
}
WebFrame* InjectedBundleHitTestResult::frame() const
{
    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return nullptr;

    Frame* frame = node->document().frame();
    if (!frame)
        return nullptr;

    return WebFrame::fromCoreFrame(*frame);
}
Example #20
0
void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, const String& text)
{
    if (!object || text.isEmpty())
        return;

    AccessibilityObject* parentObject = object->parentObjectUnignored();
    if (!parentObject)
        return;

    AtkObject* wrapper = parentObject->wrapper();
    if (!wrapper || !ATK_IS_TEXT(wrapper))
        return;

    Node* node = object->node();
    if (!node)
        return;

    // Ensure document's layout is up-to-date before using TextIterator.
    Document& document = node->document();
    document.updateLayout();

    // Select the right signal to be emitted
    CString detail;
    switch (textChange) {
    case AXTextInserted:
        detail = "text-insert";
        break;
    case AXTextDeleted:
        detail = "text-remove";
        break;
    case AXTextAttributesChanged:
        detail = "text-attributes-changed";
        break;
    }

    String textToEmit = text;
    unsigned offsetToEmit = offset;

    // If the object we're emitting the signal from represents a
    // password field, we will emit the masked text.
    if (parentObject->isPasswordField()) {
        String maskedText = parentObject->passwordFieldValue();
        textToEmit = maskedText.substring(offset, text.length());
    } else {
        // Consider previous text objects that might be present for
        // the current accessibility object to ensure we emit the
        // right offset (e.g. multiline text areas).
        RefPtr<Range> range = Range::create(document, node->parentNode(), 0, node, 0);
        offsetToEmit = offset + TextIterator::rangeLength(range.get());
    }

    g_signal_emit_by_name(wrapper, detail.data(), offsetToEmit, textToEmit.length(), textToEmit.utf8().data());
}
Example #21
0
bool LayoutTheme::isActive(const LayoutObject& o)
{
    Node* node = o.node();
    if (!node)
        return false;

    Page* page = node->document().page();
    if (!page)
        return false;

    return page->focusController().isActive();
}
WebFrame* InjectedBundleHitTestResult::frame() const
{
    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return 0;

    Frame* frame = node->document().frame();
    if (!frame)
        return 0;

    WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(frame->loader().client());
    return webFrameLoaderClient ? webFrameLoaderClient->webFrame() : 0;
}
void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>* timer)
{
    // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate
    // updateRendering.  It will then call back to us with new information.
    bool animating = false;
    RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
    for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
        CompositeAnimation* compAnim = it->second;
        if (!compAnim->suspended() && compAnim->animating()) {
            animating = true;
            compAnim->setAnimating(false);

            Node* node = it->first->element();
            ASSERT(!node || (node->document() && !node->document()->inPageCache()));
            node->setChanged(AnimationStyleChange);
        }
    }

    m_frame->document()->updateRendering();

    updateAnimationTimer();
}
Example #24
0
DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const
{
    HBITMAP result = 0;
    if (m_dragImage) {
        result = createDragImageFromImage(m_dragImage->image());        
        loc = m_dragLoc;
    } else if (m_dragImageElement) {
        Node* node = m_dragImageElement.get();
        result = node->document()->frame()->nodeImage(node);
        loc = m_dragLoc;
    }
    return result;
}
Example #25
0
void SelectionController::nodeWillBeRemoved(Node *node)
{
    if (isNone())
        return;
    
    Node* base = m_sel.base().node();
    Node* extent = m_sel.extent().node();
    Node* start = m_sel.start().node();
    Node* end = m_sel.end().node();
    
    bool baseRemoved = node == base || (base && base->isAncestor(node));
    bool extentRemoved = node == extent || (extent && extent->isAncestor(node));
    bool startRemoved = node == start || (start && start->isAncestor(node));
    bool endRemoved = node == end || (end && end->isAncestor(node));
    
    bool clearRenderTreeSelection = false;
    bool clearDOMTreeSelection = false;

    if (startRemoved || endRemoved) {
        // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away.
        clearRenderTreeSelection = true;
        clearDOMTreeSelection = true;
    } else if (baseRemoved || extentRemoved) {
        if (m_sel.isBaseFirst()) {
            m_sel.setBase(m_sel.start());
            m_sel.setExtent(m_sel.end());
        } else {
            m_sel.setBase(m_sel.start());
            m_sel.setExtent(m_sel.end());
        }
    // FIXME: This could be more efficient if we had an isNodeInRange function on Ranges.
    } else if (Range::compareBoundaryPoints(m_sel.start(), Position(node, 0)) == -1 &&
               Range::compareBoundaryPoints(m_sel.end(), Position(node, 0)) == 1) {
        // If we did nothing here, when this node's renderer was destroyed, the rect that it 
        // occupied would be invalidated, but, selection gaps that change as a result of 
        // the removal wouldn't be invalidated.
        // FIXME: Don't do so much unnecessary invalidation.
        clearRenderTreeSelection = true;
    }

    if (clearRenderTreeSelection) {
        // FIXME (6498): This doesn't notify the editing delegate of a selection change.
        RefPtr<Document> document = start->document();
        document->updateRendering();
        if (RenderView* view = static_cast<RenderView*>(document->renderer()))
            view->clearSelection();
    }

    if (clearDOMTreeSelection)
        setSelection(Selection());
}
Example #26
0
void RenderNamedFlowThread::clearContentNodes()
{
    for (NamedFlowContentNodes::iterator it = m_contentNodes.begin(); it != m_contentNodes.end(); ++it) {
        Node* contentNode = *it;
        
        ASSERT(contentNode && contentNode->isElementNode());
        ASSERT(contentNode->inNamedFlow());
        ASSERT(contentNode->document() == document());
        
        contentNode->clearInNamedFlow();
    }
    
    m_contentNodes.clear();
}
Example #27
0
void EventPath::adjustForRelatedTarget(Node& target, EventTarget* relatedTarget)
{
    if (!relatedTarget)
        return;
    Node* relatedNode = relatedTarget->toNode();
    if (!relatedNode)
        return;
    if (target.document() != relatedNode->document())
        return;
    if (!target.inDocument() || !relatedNode->inDocument())
        return;

    RelatedTargetMap relatedNodeMap;
    buildRelatedNodeMap(*relatedNode, relatedNodeMap);

    for (const auto& treeScopeEventContext : m_treeScopeEventContexts) {
        EventTarget* adjustedRelatedTarget = findRelatedNode(treeScopeEventContext->treeScope(), relatedNodeMap);
        ASSERT(adjustedRelatedTarget);
        treeScopeEventContext.get()->setRelatedTarget(adjustedRelatedTarget);
    }

    shrinkIfNeeded(target, *relatedTarget);
}
Example #28
0
RefPtr<LegacyWebArchive> LegacyWebArchive::create(Node& node, std::function<bool (Frame&)> frameFilter)
{
    Frame* frame = node.document().frame();
    if (!frame)
        return create();

    // If the page was loaded with javascript enabled, we don't want to archive <noscript> tags
    // In practice we don't actually know whether scripting was enabled when the page was originally loaded
    // but we can approximate that by checking if scripting is enabled right now.
    std::unique_ptr<Vector<QualifiedName>> tagNamesToFilter;
    if (frame->page() && frame->page()->settings().isScriptEnabled()) {
        tagNamesToFilter = std::make_unique<Vector<QualifiedName>>();
        tagNamesToFilter->append(HTMLNames::noscriptTag);
    }

    Vector<Node*> nodeList;
    String markupString = createMarkup(node, IncludeNode, &nodeList, DoNotResolveURLs, tagNamesToFilter.get());
    Node::NodeType nodeType = node.nodeType();
    if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
        markupString = documentTypeString(node.document()) + markupString;

    return create(markupString, *frame, nodeList, WTFMove(frameFilter));
}
Example #29
0
String createMarkup(const Node& node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip, EFragmentSerialization fragmentSerialization)
{
    HTMLElement* deleteButtonContainerElement = 0;
#if ENABLE(DELETION_UI)
    if (Frame* frame = node.document().frame()) {
        deleteButtonContainerElement = frame->editor().deleteButtonController().containerElement();
        if (node.isDescendantOf(deleteButtonContainerElement))
            return emptyString();
    }
#endif

    MarkupAccumulator accumulator(nodes, shouldResolveURLs, 0, fragmentSerialization);
    return accumulator.serializeNodes(const_cast<Node&>(node), deleteButtonContainerElement, childrenOnly, tagNamesToSkip);
}
void WebPage::findZoomableAreaForPoint(const IntPoint& point, const IntSize& area)
{
    UNUSED_PARAM(area);
    Frame* mainframe = m_mainFrame->coreFrame();
    HitTestResult result = mainframe->eventHandler().hitTestResultAtPoint(mainframe->view()->windowToContents(point), HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent);

    Node* node = result.innerNode();

    if (!node)
        return;

    IntRect zoomableArea = node->pixelSnappedBoundingBox();

    while (true) {
        bool found = !node->isTextNode() && !node->isShadowRoot();

        // No candidate found, bail out.
        if (!found && !node->parentNode())
            return;

        // Candidate found, and it is a better candidate than its parent.
        // NB: A parent is considered a better candidate iff the node is
        // contained by it and it is the only child.
        if (found && (!node->parentNode() || node->parentNode()->childNodeCount() != 1))
            break;

        node = node->parentNode();
        zoomableArea.unite(node->pixelSnappedBoundingBox());
    }

    if (node->document().frame() && node->document().frame()->view()) {
        const ScrollView* view = node->document().frame()->view();
        zoomableArea = view->contentsToWindow(zoomableArea);
    }

    send(Messages::WebPageProxy::DidFindZoomableArea(point, zoomableArea));
}